Home > Software engineering >  a type problem when define customEvent in typescript in strict mode
a type problem when define customEvent in typescript in strict mode

Time:08-02

my tsconfig.json has followed fields

  "compilerOptions": {
      ...
      "strictNullChecks": true,
      "strict": true,
      "alwaysStrict": true,
      "noImplicitReturns": true,
      "noImplicitThis": true, 
      ...
}

and I designed the followed code

/*
constants in ./events
export const EVENT_NEED_MASK="EVENT_NEED_MASK"
export const EVENT_NO_MASK="EVENT_NO_MASK"
export const EVENT_FRAME_DRAWING_TRIGGERED="EVENT_FRAME_DRAWING_TRIGGERED"
export const EVENT_FRAME_DRAWING_SATRTED="EVENT_FRAME_DRAWING_SATRTED"
export const EVENT_FRAME_DRAWING_MOVING="EVENT_FRAME_DRAWING_MOVING"
...
*/

import * as EVENTS from "./events"

export type GlobalEventName = keyof GlobalEventHandlersEventMap
export type ExtendEventName =
    keyof GlobalEventName
    & keyof typeof EVENTS 
export type EventEmitter = {
    [prop in  ExtendEventName]:EventListener
}

export function InstallEvent(e: EventEmitter, place:Window|HTMLElement = window) {
    Object.keys(e).map(
        (key:string  ) => {
                place.addEventListener(key, (x:Event) => {
                    e[key](x)
                }
            )
        }
    )
}

export type UserAction ={
    AtBody?:EventEmitter,
    AtShadowRoot?:EventEmitter
}

export let ActionSet: UserAction = {
    AtBody: {
        keydown: (e: KeyboardEvent) => {
            
        },
        mousedown: (e: MouseEvent & DomEvent) => {
            
        },
        mousemove: (e: MouseEvent & DomEvent) => {
            
        },
        mouseup: (e: MouseEvent & DomEvent) => {
           
        },
        click:(e: MouseEvent & DomEvent) =>{
        }
    },
    AtShadowRoot: {
        click: (e: MouseEvent & DomEvent) => {
            
        },
        mousedown: (e: MouseEvent & DomEvent) => {
            
        },
        dblclick: (e: MouseEvent & DomEvent) => {
            
        }
    }
}

export let InstallAction = (act: UserAction) => {
    let place = {
        AtBody: window,
        AtShadowRoot: CORE.ShadowRoot
    }
    Object.keys(act).forEach((at) => {
        InstallEvent(act[at], place[at])
    })
}



My IDE told me that:

TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'EventEmitter'.   No index signature with a parameter of type 'string' was found on type 'EventEmitter'.

I don't know how to fix this problem enter image description here

enter image description here

CodePudding user response:

The problem is that your key in Object.keys(e).map((key: string) => ... is a type string while EventEmitter expects ExtendEventName type.

The way I'd refactor this is to change ExtendedEventName and EventEmitter type to this:

export type ExtendEventName = keyof (GlobalEventHandlersEventMap & typeof EVENTS);
export type EventEmitter = Record<ExtendEventName | string, EventListener>;

As far as I know, there doesn't seem to be a way to iterate over a typed objects keys and have the key be typed. It'll always be string unfortunately so we need to have string fallback in the EventEmitter key.

Edit: You can verify this by doing

for (const key in EventEmitter) {
  // notice that `key` is type `string` not `ExtendEventName`
  // although if you check `EventEmitter`
  // it'll show all valid keys (`[key: ExtendEventName]: EventListener`). 
  // not `[key: string]: EventListener]`
}
  • Related