关于TypeScript Playground中泛型回调类型提示正确性的问询
TypeScript泛型函数参数类型推断疑问
先看这段代码:
interface MyMouseEvent { x: number; y: number; } interface MyKeyboardEvent { key: string; } interface MyEventObjects { click: MyMouseEvent; keypress: MyKeyboardEvent; } function handleEvent<K extends keyof MyEventObjects>( eventName: K, callback: (e: MyEventObjects[K]) => void ) { } handleEvent(); handleEvent('click', (e: MyMouseEvent) => {});
当无参数调用handleEvent()时,错误提示里callback的类型是(e: MyMouseEvent | MyKeyboardEvent) => void。但按照协变逻辑,如果这是callback的正确类型,那第二个调用(传入'click'和MyMouseEvent类型的回调)应该报错,可实际并没有。
为验证这个矛盾,我写了测试代码:
function test(f: (e: MyMouseEvent | MyKeyboardEvent) => void): void {} test('click', (e: MyMouseEvent) => {});
调用test时确实出现了预期的错误。所以我认为,无参数调用handleEvent时,callback的正确提示类型应该是(e: MyMouseEvent) => void | (e: MyKeyboardEvent) => void,这个判断对吗?
你的判断是正确的,结合TypeScript的规则可以解释清楚:
- 无参数调用
handleEvent()时,泛型参数K会被推断为keyof MyEventObjects(即"click" | "keypress")。 - 这里的关键是函数参数处于逆变位置:如果直接把
MyEventObjects[K]替换成MyMouseEvent | MyKeyboardEvent,得到的(e: MyMouseEvent | MyKeyboardEvent) => void要求回调能处理两种事件的所有情况,但(e: MyMouseEvent) => void显然做不到——这就是test函数报错的原因。 - 但TypeScript对泛型联合类型的逆变位置有特殊处理:当
K是联合类型时,(e: MyEventObjects[K]) => void会自动展开为函数类型的联合,也就是(e: MyMouseEvent) => void | (e: MyKeyboardEvent) => void。这种类型允许回调是能处理任意一种事件的函数,完全符合泛型函数的预期行为。 - 错误提示里显示的
(e: MyMouseEvent | MyKeyboardEvent) => void只是TypeScript的简化展示,实际生效的是函数类型联合。这也是第二个调用handleEvent('click', ...)不报错的原因——此时K被推断为"click",回调类型恰好是联合类型中的一个成员,完全匹配。
内容的提问来源于stack exchange,提问作者Jared Smith
相关产品推荐
相关产品推荐

