Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Typescript 如何推断记录的键<;字符串,未知[]>;打字_Typescript - Fatal编程技术网

Typescript 如何推断记录的键<;字符串,未知[]>;打字

Typescript 如何推断记录的键<;字符串,未知[]>;打字,typescript,Typescript,我一直在读《编程打字脚本》。在书中的一章中,我遇到了这个问题 我正在尝试实现EventEmitter的包装器,它对事件名称及其参数有限制。代码使用泛型实现可重用性 书中的代码: 从“事件”导入EventEmitter 类安全发射器< 事件扩展记录 > { 私有发射器=新事件发射器 散发( 频道:K,, …数据:事件[K] ) { 返回此.emitter.emit(通道,…数据) } 在( 频道:K,, 侦听器:(…数据:事件[K])=>void ) { 返回this.emitter.on(通道,

我一直在读《编程打字脚本》。在书中的一章中,我遇到了这个问题

我正在尝试实现EventEmitter的包装器,它对事件名称及其参数有限制。代码使用泛型实现可重用性

书中的代码:

从“事件”导入EventEmitter
类安全发射器<
事件扩展记录
> {
私有发射器=新事件发射器
散发(
频道:K,,
…数据:事件[K]
) {
返回此.emitter.emit(通道,…数据)
}
在(
频道:K,,
侦听器:(…数据:事件[K])=>void
) {
返回this.emitter.on(通道,侦听器)
}
}
错误:

(parameter) channel: K extends keyof Events
Argument of type 'K' is not assignable to parameter of type 'string | symbol'.
  Type 'keyof Events' is not assignable to type 'string | symbol'.
    Type 'string | number | symbol' is not assignable to type 'string | symbol'.
      Type 'number' is not assignable to type 'string | symbol'.
        Type 'keyof Events' is not assignable to type 'symbol'.
          Type 'K' is not assignable to type 'symbol'.
            Type 'keyof Events' is not assignable to type 'symbol'.
              Type 'string | number | symbol' is not assignable to type 'symbol'.
                Type 'string' is not assignable to type 'symbol'.ts(2345)
根据我的快速搜索,
keyof
操作符总是推断
string | symbol | number
。所以我修改了代码来推断字符串

on(
这适用于上面的错误。但是,我有另一个错误:

(parameter) listener: (...data: Events[K]) => void
Argument of type '(...data: Events[K]) => void' is not assignable to parameter of type '(...args: any[]) => void'.
  Types of parameters 'data' and 'args' are incompatible.
    Type 'any[]' is not assignable to type 'Events[K]'.ts(2345)
没有类型断言,有没有办法解决这个问题


(tsconfig.json)


我已在

上上载了代码。如果您正在尝试创建类型安全的
EventEmitter
实现,这将帮助您开始:

import EventEmitter from "events";

type EventsOf<T> = keyof T & string;

type ListenerFunc<T, E extends EventsOf<T>> =
  T[E] extends (...args: any[]) => void ? T[E] : never;

type ListenerArgs<T, E extends EventsOf<T>> =
  T[E] extends (...args: infer A) => void ? A : never;

class SafeEmitter<T> extends EventEmitter {
  on<E extends EventsOf<T>>(event: E, listener: ListenerFunc<T, E>) {
    return super.on(event, listener);
  }

  once<E extends EventsOf<T>>(event: E, listener: ListenerFunc<T, E>) {
    return super.once(event, listener);
  }

  emit<E extends EventsOf<T>>(event: E, ...args: ListenerArgs<T, E>) {
    return super.emit(event, ...args);
  }
}

// === Let's test it! ===

interface MyEvents {
  started: () => void;
  received: (foo: string, bar?: number) => void;
}

class MyEmitter extends SafeEmitter<MyEvents> { }

const emitter = new MyEmitter();
emitter.on("started", () => console.log("started"));             // OK
emitter.on("received", (foo, bar) => console.log([foo, bar]));   // OK
emitter.on("invalid", () => false);                              // ERROR
emitter.emit("started");                                         // OK
emitter.emit("started", "ERROR!");                               // ERROR
emitter.emit("received");                                        // ERROR
emitter.emit("received", "FOO");                                 // OK
emitter.emit("received", "FOO", 42);                             // OK
emitter.emit("received", "FOO", "ERROR!");                       // ERROR
emitter.emit("invalid");                                         // ERROR
从“事件”导入EventEmitter;
类型EventsOf=keyof T&string;
类型ListenerFunc=
T[E]扩展(…args:any[])=>void?T[E]:从不;
类型ListenerArgs=
T[E]扩展(…参数:推断A)=>void?A:从不;
类SafeEmitter扩展了EventEmitter{
打开(事件:E,侦听器:ListenerFunc){
返回super.on(事件、侦听器);
}
一次(事件:E,侦听器:ListenerFunc){
返回super.one(事件、侦听器);
}
发射(事件:E,…参数:ListenerArgs){
返回super.emit(事件,…参数);
}
}
//让我们测试一下===
接口事件{
开始:()=>无效;
接收:(foo:string,bar?:number)=>无效;
}
类MyEmitter扩展了SafeEmitter{}
常量发射器=新的MyMitter();
emitter.on(“启动”),()=>console.log(“启动”);//确定
emitter.on(“received”,(foo,bar)=>console.log([foo,bar]);//确定
发射器.on(“无效”,()=>false);//错误
emitter.emit(“启动”);//确定
emit(“启动”,“错误!”);//错误
emitter.emit(“received”);//错误
emitter.emit(“received”,“FOO”);//确定
emit(“received”,“FOO”,42);//确定
emit(“已接收”、“FOO”、“ERROR!”);//错误
emitter.emit(“无效”);//错误
为了回答您的问题,TypeScript在将
any[]
赋值给无界泛型类型时通常比必要的更严格。在这种特殊情况下,它无法检测到
事件的属性值必须是数组


我发现的唯一解决方法是将事件侦听器函数定义为
事件
接口上的实际方法,这正是上面示例中发生的情况。作为额外的好处,您还可以指定事件参数名称,而不是默认为
数据0
数据1
,等等。

ting第二个错误。您运行的是什么版本的typescript?@Wise草莓谢谢您的评论!我使用的版本是
3.7.2
。我将把我的tsconfig.json
侦听器:(…数据:事件[])=>void
这不是你想要的吗?另外,鉴于你对TypeScript还不熟悉,我敢打赌自己构建这个更为有价值,这里可以找到另一个更简单的实现:(我实际上在我的一个项目中使用它)谢谢你的回答!使用你的
ListenerFunc
作为参数类型对我很有效。
import EventEmitter from "events";

type EventsOf<T> = keyof T & string;

type ListenerFunc<T, E extends EventsOf<T>> =
  T[E] extends (...args: any[]) => void ? T[E] : never;

type ListenerArgs<T, E extends EventsOf<T>> =
  T[E] extends (...args: infer A) => void ? A : never;

class SafeEmitter<T> extends EventEmitter {
  on<E extends EventsOf<T>>(event: E, listener: ListenerFunc<T, E>) {
    return super.on(event, listener);
  }

  once<E extends EventsOf<T>>(event: E, listener: ListenerFunc<T, E>) {
    return super.once(event, listener);
  }

  emit<E extends EventsOf<T>>(event: E, ...args: ListenerArgs<T, E>) {
    return super.emit(event, ...args);
  }
}

// === Let's test it! ===

interface MyEvents {
  started: () => void;
  received: (foo: string, bar?: number) => void;
}

class MyEmitter extends SafeEmitter<MyEvents> { }

const emitter = new MyEmitter();
emitter.on("started", () => console.log("started"));             // OK
emitter.on("received", (foo, bar) => console.log([foo, bar]));   // OK
emitter.on("invalid", () => false);                              // ERROR
emitter.emit("started");                                         // OK
emitter.emit("started", "ERROR!");                               // ERROR
emitter.emit("received");                                        // ERROR
emitter.emit("received", "FOO");                                 // OK
emitter.emit("received", "FOO", 42);                             // OK
emitter.emit("received", "FOO", "ERROR!");                       // ERROR
emitter.emit("invalid");                                         // ERROR