看看这个简单的示例代码:
interface EventEmitter<ListenersT>
{
on<EventT extends keyof ListenersT>(event: EventT, listener: ListenersT[EventT]): this;
}
interface MyEvents
{
foo(x: number): void;
bar(): void;
moo(a: string, b: Date): void;
}
interface MyEmitter extends EventEmitter<MyEvents>
{}
const my: MyEmitter = <any> {};
my.on('foo', (x) => x / 4); // Parameter 'x' implicitly has an 'any' type.
my.on('bar', () => 42);
my.on('moo', (a, b) => a + ': ' + b.getFullYear());
我想知道为什么在my.on('foo', ...)
调用中,编译器无法推断x
的类型,而对于my.on('moo', ...)
调用中的两个参数都可以正常工作。
您可以去测试此on the Typescript Playground。您可以通过按 Options 按钮来启用noImplicitAny
标志,以获取我在注释中添加的警告,或者将鼠标悬停在代码窗口中的x
参数上以查看该警告未能推断出number
类型。
奖金:如果我在foo()
中添加另一个参数,这将变得更加奇怪。在这种情况下,它也无法推断moo
的参数类型。
看来,MyEvents
中的函数类型只能推断出最多的参数,并且只有它是唯一具有该数量的参数的类型。
答案 0 :(得分:1)
当回调的类型取决于另一个参数时,Typescript很难将参数推导给回调,我在几个SO问题中已经看到了这一点(如果需要,我可以搜索一些问题)。
解决方案是以不同的方式提出问题,即使on
函数具有多个重载。诀窍是这样做而不必从接口重新声明所有签名。
您的要求与this答案非常相似,您将在此处找到更详细的说明,但根据您的需要修改后的代码将如下所示:
interface EventEmitter<ListenersT>
{
on : OnAll<ListenersT, this>
}
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type OnSignatures<T, TReturn> = { [P in keyof T]: (event: P, listener: T[P]) => TReturn }
type OnAll<T, TReturn> = UnionToIntersection<OnSignatures<T, TReturn>[keyof T]>
interface MyEvents
{
foo(x: number): void;
bar(): void;
moo(a: string, b: Date): void;
}
interface MyEmitter extends EventEmitter<MyEvents>
{
// on is now equivalent to an on with these overloads
//on(event: 'foo', listener: (x: number) => void): this;
//on(event: 'bar', listener: () => void): this;
//on(event: 'moo', listener: (a: string, b: Date)=> void): this;
}
const my: MyEmitter = <any> {};
my.on('foo', (x) => x / 4); // x is number
my.on('bar', () => 42);
my.on('moo', (a, b) => a + ': ' + b.getFullYear());