如何让TypeScript在值类型为多接口联合时正确推断类型?
TypeScript联合类型属性访问的类型推断问题
问题场景
定义了两个接口及包含它们的联合类型:
interface KeyA { a: ValueA; } interface KeyB { b: ValueB; } type KeyExpr = KeyA | KeyB | Record<string, never>;
需求是访问KeyExpr实例的a或b属性时,能得到对应的ValueA或ValueB类型,但当前代码中属性访问的类型推断不符合预期:
function func(keyExpr: KeyExpr) { const value = keyExpr['a']; // 当前value的类型为never,而非预期的ValueA }
已通过工具类型KnownKeys获取到联合类型的有效键类型"a" | "b",但直接用该类型访问属性时,类型仍被解析为never:
export type KnownKeys<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : K; } extends infer R ? R extends { [_ in keyof T]: infer U } ? U : never : never; type ValidKeys = KnownKeys<KeyExpr>; // 类型为"a" | "b"
解决方案
1. 用类型守卫缩小类型范围
通过自定义类型守卫函数,判断实例具体属于哪个接口,在守卫后的代码块中访问属性即可得到正确类型:
function isKeyA(obj: KeyExpr): obj is KeyA { return 'a' in obj; } function isKeyB(obj: KeyExpr): obj is KeyB { return 'b' in obj; } function func(keyExpr: KeyExpr) { if (isKeyA(keyExpr)) { const value = keyExpr.a; // 类型为ValueA } else if (isKeyB(keyExpr)) { const value = keyExpr.b; // 类型为ValueB } }
2. 替换Record<string, never>为更严格的空类型
Record<string, never>会导致联合类型的属性访问被推断为never,可将其替换为明确的空接口或{},避免干扰:
interface EmptyObject {} type KeyExpr = KeyA | KeyB | EmptyObject; // 此时访问keyExpr['a']的类型为ValueA | undefined function func(keyExpr: KeyExpr) { if ('a' in keyExpr) { const value = keyExpr['a']; // 类型为ValueA } }
3. 工具函数结合条件类型实现类型安全访问
定义工具类型提取对应键的类型,再通过工具函数封装访问逻辑,配合类型断言辅助推断:
type ValueForKey<T, K extends KnownKeys<T>> = T extends { [key in K]: infer V } ? V : never; function getValue<K extends KnownKeys<KeyExpr>>( keyExpr: KeyExpr, key: K ): ValueForKey<KeyExpr, K> { return keyExpr[key] as ValueForKey<KeyExpr, K>; } // 使用示例 const expr: KeyExpr = { a: {} as ValueA }; const valueA = getValue(expr, 'a'); // 类型为ValueA const valueB = getValue(expr, 'b'); // 类型为ValueB
原因说明
原问题中Record<string, never>的所有属性访问结果均为never,当联合类型中包含该类型时,TypeScript会对所有成员的索引结果取联合,最终导致keyExpr['a']的类型被推断为ValueA | undefined | never,而never会被联合类型吸收,但实际场景中因类型解析逻辑,最终表现为never。通过上述方法规避Record<string, never>的干扰或缩小类型范围,即可得到正确的类型推断。
内容的提问来源于stack exchange,提问作者Edy Bourne
相关产品推荐
相关产品推荐

