From a94c5115a8fdde6115733a0eadaf926f199f822b Mon Sep 17 00:00:00 2001 From: "Joshua A. Horton" Date: Thu, 16 May 2024 15:05:23 +0700 Subject: [PATCH] chore(web): enforces strict function types in web/ --- web/src/app/browser/src/keymanEngine.ts | 8 ++++---- web/src/app/browser/src/languageMenu.ts | 10 ++++++---- web/src/app/browser/src/utils/alertHost.ts | 8 ++++---- web/src/app/ui/kmwuitoolbar.ts | 10 +++++----- web/src/engine/dom-utils/src/cookieSerializer.ts | 5 ++--- web/src/engine/events/src/legacyEventEmitter.ts | 4 +++- web/src/engine/osk/src/banner/suggestionBanner.ts | 2 +- web/src/engine/osk/src/input/gestures/browser/flick.ts | 6 +++--- .../osk/src/keyboard-layout/gesturePreviewHost.ts | 6 +++--- .../engine/attachment/pageContextAttachment.tests.ts | 4 ++-- web/src/tsconfig.dom.json | 1 - 11 files changed, 33 insertions(+), 31 deletions(-) diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index 515c5965a28..b80a80baa94 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -57,11 +57,11 @@ export default class KeymanEngine extends KeymanEngineBase this.legacyAPIEvents), (engine: KeymanEngine) => { + super(worker, config, new ContextManager(config, () => this.legacyAPIEvents), (engine) => { return { // The `engine` parameter cannot be supplied with the constructing instance before calling // `super`, hence the 'fun' rigging to supply it _from_ `super` via this closure. - keyboardInterface: new KeyboardInterface(window, engine), + keyboardInterface: new KeyboardInterface(window, engine as KeymanEngine), defaultOutputRules: new DefaultBrowserRules(engine.contextManager) }; }); @@ -73,8 +73,8 @@ export default class KeymanEngine extends KeymanEngineBase) => { - const e = target?.getElement(); + this.contextManager.on('targetchange', (target) => { + const e = (target as OutputTarget)?.getElement(); if(this.osk) { (this.osk.activationModel as TwoStateActivator).activationTrigger = e; } diff --git a/web/src/app/browser/src/languageMenu.ts b/web/src/app/browser/src/languageMenu.ts index 83222a19d34..7745d46efd1 100644 --- a/web/src/app/browser/src/languageMenu.ts +++ b/web/src/app/browser/src/languageMenu.ts @@ -482,7 +482,8 @@ export class LanguageMenu { } // Touchstart (or mspointerdown) event highlights the touched list item - const touchStart = function(this: HTMLElement & KeyboardTag, e: TouchEvent) { + const touchStart = function(this: HTMLElement, e: TouchEvent) { + e.stopPropagation(); if(this.className.indexOf('selected') <= 0) { this.className=this.className+' selected'; @@ -495,7 +496,7 @@ export class LanguageMenu { //TODO: Still drags Android background sometimes (not consistently) // Touchmove drags the list and prevents release from selecting the language - const touchMove=function(this: HTMLElement & KeyboardTag, e: TouchEvent) { + const touchMove=function(this: HTMLElement, e: TouchEvent) { e.stopImmediatePropagation(); var scroller=languageMenu.lgList.childNodes[0], yMax=scroller.scrollHeight-scroller.offsetHeight, @@ -539,7 +540,8 @@ export class LanguageMenu { }; // Touch release (click) event selects touched list item - const touchEnd=function(this: HTMLElement & KeyboardTag, e: TouchEvent) { + const touchEnd=function(this: HTMLElement, e: TouchEvent) { + const entry = this as HTMLElement & KeyboardTag; if(typeof(e.stopImmediatePropagation) != 'undefined') { e.stopImmediatePropagation(); } else { @@ -552,7 +554,7 @@ export class LanguageMenu { languageMenu.keyman.contextManager.focusAssistant.setFocusTimer(); // #5946 languageMenu.lgList.style.display='none'; //still allows blank menu momentarily on selection - languageMenu.keyman.contextManager.activateKeyboard(this.kn,this.kc,true); + languageMenu.keyman.contextManager.activateKeyboard(entry.kn, entry.kc,true); languageMenu.keyman.contextManager.restoreLastActiveTarget(); languageMenu.hide(); } diff --git a/web/src/app/browser/src/utils/alertHost.ts b/web/src/app/browser/src/utils/alertHost.ts index bc28926949c..d97590c455a 100644 --- a/web/src/app/browser/src/utils/alertHost.ts +++ b/web/src/app/browser/src/utils/alertHost.ts @@ -58,7 +58,7 @@ export class AlertHost { bx.className='kmw-alert-close'; // Close alert if anywhere in box is touched, since close box is too small on mobiles - lb.onmousedown = lb.onclick = (e) => { + const lbClick = lb.onmousedown = lb.onclick = (e: MouseEvent | TouchEvent) => { // Ignore if waiting, only handle for alert if(bx.style.display == 'block') { bg.style.display='none'; @@ -68,13 +68,13 @@ export class AlertHost { } }; - lb.addEventListener('touchstart', lb.onclick, false); - bg.onmousedown = bg.onclick = (e) => { + lb.addEventListener('touchstart', lbClick, false); + const bgClick = bg.onmousedown = bg.onclick = (e: MouseEvent | TouchEvent) => { e.preventDefault(); e.stopPropagation(); } - bg.addEventListener('touchstart', bg.onclick, false); + bg.addEventListener('touchstart', bgClick, false); lb.appendChild(bx); // [0] lb.appendChild(lt); // [1] lb.appendChild(gr); // [2] diff --git a/web/src/app/ui/kmwuitoolbar.ts b/web/src/app/ui/kmwuitoolbar.ts index 3a0039eeebe..acfa822f0c9 100644 --- a/web/src/app/ui/kmwuitoolbar.ts +++ b/web/src/app/ui/kmwuitoolbar.ts @@ -1187,7 +1187,7 @@ if(!keyman?.ui?.name) { * @param {Object} p **/ readonly onHideOSK = (p: { - HiddenByUser: boolean + HiddenByUser?: boolean }) => { if(this.init && p.HiddenByUser) { this.oskButtonNode.className = 'kmw_button'; @@ -1264,7 +1264,7 @@ if(!keyman?.ui?.name) { * A closure to be evaluated upon dismissal of a modal popup generated * by this UI module. */ - dismissalCallback: (event: Event) => any = null; + dismissalCallback: (event: MouseEvent) => any = null; /** * The root element associated with an active modal popup; related @@ -1277,7 +1277,7 @@ if(!keyman?.ui?.name) { * for the popup, we stash the old event listener here and restore * it when we're done. */ - lastDismissalCallback: (event: Event) => any = null; + lastDismissalCallback: (event: MouseEvent) => any = null; /** * Function PopupDismissal @@ -1309,7 +1309,7 @@ if(!keyman?.ui?.name) { * @param {function(Object)} callback * Description Prepare for callback dismissal **/ - SetupPopupDismissal(element: HTMLElement, callback: (event: Event) => any) { + SetupPopupDismissal(element: HTMLElement, callback: (event: MouseEvent) => any) { if(this.PopupDismissal == document.onclick) { this.CancelPopupDismissal(this.dismissalCallback); } @@ -1325,7 +1325,7 @@ if(!keyman?.ui?.name) { * @param {?function(Object)} callback * Description Cancel callback dismissal **/ - CancelPopupDismissal(callback?: (event: Event) => void) { + CancelPopupDismissal(callback?: (event: MouseEvent) => void) { if(this.PopupDismissal == document.onclick) { document.onclick = this.lastDismissalCallback; this.lastDismissalCallback = null; diff --git a/web/src/engine/dom-utils/src/cookieSerializer.ts b/web/src/engine/dom-utils/src/cookieSerializer.ts index a7a02630273..f8f830bd5cd 100644 --- a/web/src/engine/dom-utils/src/cookieSerializer.ts +++ b/web/src/engine/dom-utils/src/cookieSerializer.ts @@ -2,7 +2,6 @@ type DecodedCookieFieldValue = string | number | boolean; type FilteredRecordEncoder = (value: DecodedCookieFieldValue, key: string) => string; type FilteredRecordDecoder = (value: string, key: string) => DecodedCookieFieldValue; -const no_change = (val: string) => val as string; export default class CookieSerializer> { readonly name: string; @@ -12,11 +11,11 @@ export default class CookieSerializer val as DecodedCookieFieldValue)) as Type; } save(cookie: Type, encoder?: FilteredRecordEncoder) { - this.saveCookie(this.name, cookie, encoder || no_change); + this.saveCookie(this.name, cookie, encoder || ((val: DecodedCookieFieldValue) => val as string)); } /** diff --git a/web/src/engine/events/src/legacyEventEmitter.ts b/web/src/engine/events/src/legacyEventEmitter.ts index 2be0467cb9d..05b02052ee0 100644 --- a/web/src/engine/events/src/legacyEventEmitter.ts +++ b/web/src/engine/events/src/legacyEventEmitter.ts @@ -76,7 +76,9 @@ export class LegacyEventEmitter { func: EventListener ): boolean { this._removeEventListener(event, func); - this.events[event].push(func); + // TS gets hung up on the type info here because we can potentially store + // different types of listeners for different events. + this.events[event].push(func as unknown as any); return true; } diff --git a/web/src/engine/osk/src/banner/suggestionBanner.ts b/web/src/engine/osk/src/banner/suggestionBanner.ts index 56a4e76343f..d35504e5e24 100644 --- a/web/src/engine/osk/src/banner/suggestionBanner.ts +++ b/web/src/engine/osk/src/banner/suggestionBanner.ts @@ -477,7 +477,7 @@ export class SuggestionBanner extends Banner { maxRoamingBounds: safeBounds, safeBounds: safeBounds, // touchEventRoot: this.element, // is the default - itemIdentifier: (sample, target: HTMLElement) => { + itemIdentifier: (sample) => { const selBounds = this.selectionBounds.getBoundingClientRect(); // Step 1: is the coordinate within the range we permit for selecting _anything_? diff --git a/web/src/engine/osk/src/input/gestures/browser/flick.ts b/web/src/engine/osk/src/input/gestures/browser/flick.ts index a670c207a28..9de16058822 100644 --- a/web/src/engine/osk/src/input/gestures/browser/flick.ts +++ b/web/src/engine/osk/src/input/gestures/browser/flick.ts @@ -271,10 +271,10 @@ export default class Flick implements GestureHandler { coord: [NaN, 0] }]; - keys = keys.concat(Object.keys(flickSet).map((dir: (typeof OrderedFlickDirections[number])) => { + keys = keys.concat(Object.keys(flickSet).map((dir) => { return { - spec: flickSet[dir] as ActiveSubKey, - coord: FlickNameCoordMap.get(dir) + spec: flickSet[dir as typeof OrderedFlickDirections[number]] as ActiveSubKey, + coord: FlickNameCoordMap.get(dir as typeof OrderedFlickDirections[number]) }; })); diff --git a/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts b/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts index f1e8167daf0..321de328b83 100644 --- a/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts +++ b/web/src/engine/osk/src/keyboard-layout/gesturePreviewHost.ts @@ -69,15 +69,15 @@ export class GesturePreviewHost extends EventEmitter { if(keySpec.flick) { const flickSpec = keySpec.flick || {}; - Object.keys(flickSpec).forEach((dir: typeof OrderedFlickDirections[number]) => { + Object.keys(flickSpec).forEach((dir) => { const flickPreview = document.createElement('div'); flickPreview.className = 'kmw-flick-preview kmw-key-text'; - flickPreview.textContent = flickSpec[dir].text; + flickPreview.textContent = flickSpec[dir as typeof OrderedFlickDirections[number]].text; const ps /* preview style */ = flickPreview.style; // is in polar coords, origin toward north, clockwise. - const coords = FlickNameCoordMap.get(dir); + const coords = FlickNameCoordMap.get(dir as typeof OrderedFlickDirections[number]); const x = coerceZeroes(-Math.sin(coords[0])); // Put 'e' flick at left const y = coerceZeroes(Math.cos(coords[0])); // Put 'n' flick at bottom diff --git a/web/src/test/auto/headless/engine/attachment/pageContextAttachment.tests.ts b/web/src/test/auto/headless/engine/attachment/pageContextAttachment.tests.ts index 6b8c921dd64..79833e2a993 100644 --- a/web/src/test/auto/headless/engine/attachment/pageContextAttachment.tests.ts +++ b/web/src/test/auto/headless/engine/attachment/pageContextAttachment.tests.ts @@ -69,7 +69,7 @@ describe('PageContextAttachment', () => { sut.listInputs(); const expected = ['email', 'search', 'text', 'url', 'textarea']; - const types = sut.sortedInputs.map((e: HTMLInputElement) => (e.type)); + const types = sut.sortedInputs.map((e) => ((e as HTMLInputElement).type)); assert.equal(types.length, expected.length); assert.deepEqual(types, expected, `Actual [${types}]`); }); @@ -86,7 +86,7 @@ describe('PageContextAttachment', () => { sut.listInputs(); const expected = ['1', '3']; - const types = sut.sortedInputs.map((e: HTMLInputElement) => (e.id)); + const types = sut.sortedInputs.map((e) => (e as HTMLInputElement).id); assert.equal(types.length, expected.length); assert.deepEqual(types, expected, `Actual [${types}]`); }); diff --git a/web/src/tsconfig.dom.json b/web/src/tsconfig.dom.json index aa735c07dce..7dbe28c44cc 100644 --- a/web/src/tsconfig.dom.json +++ b/web/src/tsconfig.dom.json @@ -5,7 +5,6 @@ "lib": [ "DOM", "ES6"], // TODO: These override ../tsconfig.base.json settings, and so should be removed if possible, // but existing code in web/ breaks some of these settings - "strictFunctionTypes": false, "noImplicitReturns": false, "strictNullChecks": false, }