类型中缺少索引签名

Posted

技术标签:

【中文标题】类型中缺少索引签名【英文标题】: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'.

【问题讨论】:

请提供Record&lt;,&gt;类型的定义是否需要索引器?类似:[index:string] : string Record 是 TypeScript 中的内置类型。编辑:***.com/questions/51936369/… 【参考方案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 被强制执行。

以上是关于类型中缺少索引签名的主要内容,如果未能解决你的问题,请参考以下文章

Type 中缺少索引签名 - AngularJS + TypeScript 验证使用 $validators

错误 TS2349:无法调用其类型缺少调用签名的表达式。类型 ' ; ' 没有兼容的调用签名

无法调用其类型缺少调用签名的表达式...没有兼容的调用签名

无法调用其类型缺少调用签名的表达式

无法调用类型缺少调用签名的表达式...没有兼容的调用签名

Typescript中的“无法调用类型缺少调用签名的表达式”?