类型中缺少索引签名

时间:2018-11-26 13:54:06

标签: typescript

我正在尝试创建一个类型安全的EventEmitter,但是我不能强制传递给泛型的接口为EventMap类型,而不会遭受TypeScript的抱怨。

type EventHandler = () => void
type EventMap = Record<string, EventHandler>

interface EventListener {
  handler: EventHandler
  once: boolean
}

export class Emitter<Events extends EventMap> {
  private listeners = new Map<keyof Events, EventListener[]>()

  private addListener<E extends keyof EventMap>(type: E, listener: EventListener) {
    const listeners = this.listeners.get(type) || []
    this.listeners.set(type, [...listeners, listener])
  }

  @bind
  public on<E extends keyof EventMap>(type: E, handler: Events[E]) {
    this.addListener(type, { handler, once: false })
  }
}


interface TestEvents {
  test: (a: number) => void,
}

class Test extends Emitter<TestEvents> {}

给我

Type 'TestEvents' does not satisfy the constraint 'Record<string, EventHandler>'.
Index signature is missing in type 'TestEvents'.

2 个答案:

答案 0 :(得分:2)

您需要进行一些不同的约束,您希望Events的所有键都为EventHandlers,而不必让Events拥有索引签名。您可以使用以下内容:

type EventHandler = (...a: any[]) => void

interface EventListener {
    handler: EventHandler
    once: boolean
}

export class Emitter<Events extends Record<keyof Events, EventHandler>> {
    private listeners = new Map<keyof Events, EventListener[]>()

    private addListener<E extends keyof Events>(type: E, listener: EventListener) {
        const listeners = this.listeners.get(type) || []
        this.listeners.set(type, [...listeners, listener])
    }

    public on<E extends keyof Events>(type: E, handler: Events[E]) {
        this.addListener(type, { handler, once: false })
    }
}


interface TestEvents {
    test: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is number.

这种方法存在问题,但是如果地图中有多个事件(可能会),打字稿将无法推断参数类型

interface TestEvents {
    test: (a: number) => void,
    test2: (a: number) => void,
}

class Test extends Emitter<TestEvents> { }
new Test().on("test", a => a.toExponential) // a is any

这是可以解决的,但是类型变得更加复杂,有关类似的想法,请参见here

答案 1 :(得分:1)

由于Record是:

type Record<K extends string, T> = {
    [P in K]: T;
}

您缺少定义的索引:

interface TestEvents {
    test: () => void;
    [index: string] : EventHandler;
}

您的测试方法也是无效的,因为它不符合EventHandler要求,该要求不会对该方法强制使用任何参数。


如何?

export interface EventMap extends Record<string, EventHandler>{
    [index: string]: EventHandler;
    event1: () => void;
    event2: () => void;
}

现在所需的索引器存在,但是您强制类具有event1event2