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