如何强类型化事件发射器,以便从事件类型推断出处理程序的参数类型?
How to strongly type an event emitter such that the handler's parameter type is inferred from the event type?
我正在尝试定义一个强类型的事件发射器,我最想要的是从传递给 addEventHandler
函数的字符串中推断出回调的事件类型。
但到目前为止我都失败了,我想出的是从回调中推断出事件类型,而不是相反。
这是一个例子(带有 fiddle):
interface NumberEvent {
type: 'NumberEvent';
num: number;
}
interface StringEvent {
type: 'StringEvent';
str: string;
}
type AnyEvent = NumberEvent | StringEvent;
const addEventHandler = <ET extends AnyEvent>(type: ET['type'], handler: ((event: ET) => void)) => {
console.log(`added event handler for ${type}`);
}
addEventHandler('NumberEvent', (event: NumberEvent) => {
// this is cool
});
addEventHandler('NumberEvent', (event: StringEvent) => {
// this doesn't type check, good
});
addEventHandler('type does not exist', (x: any) => {
// why no type error?
});
我不明白为什么最后一行要进行类型检查,因为没有类型为 'type does not exist'
.
的 AnyEvent
实例
你能想出更好的方法来解决这个问题吗?
您可以通过 addEventHandler
通用事件类型而不是事件对象来实现此目的。
const addEventHandler = <ET extends AnyEvent['type']>(
type: ET,
handler: ((event: Extract<AnyEvent, { type: ET }>) => void)
) => {
console.log(`added event handler for ${type}`);
}
您也可以使用 AnyEvent & { type: ET }
而不是 Extract<AnyEvent & { type: ET }
你的类型没有阻止最后一种情况的原因是因为 ET
被推断为 any
。 any["type"]
仍然是 any
,所以它允许任何字符串。
以上版本仍然不会阻止某人这样做:
addEventHandler<any>('type does not exist', (x: any) => {
// explicitly providing <any>
});
您可以使用 <anything> & any
是 any
的事实来避免这种情况,但我个人不会打扰。除非故意破坏您的类型,否则没有人会在此处提供 any
。通过 any
检查,您还可以返回到您的通用:
type NotAny<T> = 0 extends (1 & T) ? never : T;
const addEventHandler = <ET extends AnyEvent>(type: NotAny<ET["type"]>, handler: ((event: ET) => void)) => {
console.log(`added event handler for ${type}`);
}
我正在尝试定义一个强类型的事件发射器,我最想要的是从传递给 addEventHandler
函数的字符串中推断出回调的事件类型。
但到目前为止我都失败了,我想出的是从回调中推断出事件类型,而不是相反。
这是一个例子(带有 fiddle):
interface NumberEvent {
type: 'NumberEvent';
num: number;
}
interface StringEvent {
type: 'StringEvent';
str: string;
}
type AnyEvent = NumberEvent | StringEvent;
const addEventHandler = <ET extends AnyEvent>(type: ET['type'], handler: ((event: ET) => void)) => {
console.log(`added event handler for ${type}`);
}
addEventHandler('NumberEvent', (event: NumberEvent) => {
// this is cool
});
addEventHandler('NumberEvent', (event: StringEvent) => {
// this doesn't type check, good
});
addEventHandler('type does not exist', (x: any) => {
// why no type error?
});
我不明白为什么最后一行要进行类型检查,因为没有类型为 'type does not exist'
.
AnyEvent
实例
你能想出更好的方法来解决这个问题吗?
您可以通过 addEventHandler
通用事件类型而不是事件对象来实现此目的。
const addEventHandler = <ET extends AnyEvent['type']>(
type: ET,
handler: ((event: Extract<AnyEvent, { type: ET }>) => void)
) => {
console.log(`added event handler for ${type}`);
}
您也可以使用 AnyEvent & { type: ET }
而不是 Extract<AnyEvent & { type: ET }
你的类型没有阻止最后一种情况的原因是因为 ET
被推断为 any
。 any["type"]
仍然是 any
,所以它允许任何字符串。
以上版本仍然不会阻止某人这样做:
addEventHandler<any>('type does not exist', (x: any) => {
// explicitly providing <any>
});
您可以使用 <anything> & any
是 any
的事实来避免这种情况,但我个人不会打扰。除非故意破坏您的类型,否则没有人会在此处提供 any
。通过 any
检查,您还可以返回到您的通用:
type NotAny<T> = 0 extends (1 & T) ? never : T;
const addEventHandler = <ET extends AnyEvent>(type: NotAny<ET["type"]>, handler: ((event: ET) => void)) => {
console.log(`added event handler for ${type}`);
}