为何TypeScript中stack[k]的类型推断为交集而非并集?
TypeScript泛型函数中索引类型推断为交叉类型的原因
下面的TypeScript代码定义了事件回调相关类型及泛型函数test,但在给stack[k]赋值时出现了不符合预期的类型推断:
export type EventCallbackStackType = { StringEvent?: Array<(data: string) => void>; ArrayEvent?: Array<(data: string[]) => void>; }; export type EventCallbackKeysType = keyof EventCallbackStackType; export type EventCallbackType<T extends EventCallbackKeysType> = Exclude<EventCallbackStackType[T], undefined>[number]; export type CallbackType = EventCallbackType<EventCallbackKeysType> const test = <T extends EventCallbackKeysType>(k: T, v: EventCallbackType<T>) => { const stack: EventCallbackStackType = {} stack[k] = [v] // 此处出现类型错误 }
此时stack[k]的类型被推断为:
((data: string) => void)[] & ((data: string[]) => void)[]
而非预期的联合类型:
((data: string) => void)[] | ((data: string[]) => void)[]
原因分析
核心问题出在TypeScript处理泛型索引访问的逻辑上:
- 当使用泛型类型
T extends EventCallbackKeysType作为索引时,TypeScript无法在编译阶段确定T到底是"StringEvent"还是"ArrayEvent",只会把它看作联合类型"StringEvent" | "ArrayEvent"的子类型。 - 在赋值场景下,TypeScript会采用"安全兜底"的推断逻辑:为了保证无论
T是哪个具体键,赋值操作都能兼容所有可能的情况,它会将stack[k]的类型推断为所有对应属性类型的交叉类型——交叉类型要求值同时满足所有类型的约束,这显然在这个场景下是不可能实现的,因为两种回调数组类型完全不兼容。 - 简单来说,TypeScript误以为
k可能同时指向两个属性(虽然运行时不可能),所以强制要求赋值的数组必须同时是两种回调类型的数组,最终得到了那个矛盾的交叉类型,导致报错。
内容的提问来源于stack exchange,提问作者Twist Lee
相关产品推荐
相关产品推荐

