【问题标题】:Index signature is missing in type类型中缺少索引签名
【发布时间】:2019-04-28 04:30:57
【问题描述】:

我正在尝试创建一个类型安全的 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'.

【问题讨论】:

标签: typescript


【解决方案1】:

您需要对其进行稍微不同的约束,您希望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 以获得非常相似的想法。

【讨论】:

  • 所以,我最终使我的发射器与传统的 node.js 发射器有点不同,方法是使用extends object 并使我的地图类型表示单个值而不是多个参数。不过谢谢你的回答。
【解决方案2】:

由于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

【讨论】:

  • 对,但这不是我需要的。在那里添加索引签名也不会使其类型安全,因为对可以发出和收听的事件没有限制。必须有一种方法可以正确地做到这一点。
  • 这是您在问题中所要求的。 EventMap 只是一个带有索引字符串和 EventHandler 作为结果的记录,所以是 EventMap 被强制执行。
猜你喜欢
  • 1970-01-01
  • 2022-08-21
  • 2020-01-17
  • 2020-06-27
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多