Typescript:如何根据第一个参数的类型推导第二个参数的对应类型
问题根因
当前TypeScript的控制流分析能力不支持对泛型参数关联的条件类型做自动窄化:通过switch(c.type)确认第一个参数的具体类型后,编译器无法同步推导State<C>的对应类型,因此访问第二个参数的特定属性会抛出类型错误。
解决方案
有两种常用的实现方式,都可以保留外部调用的类型推断能力,同时解决内部类型窄化问题:
方案1:使用元组联合类型(推荐,无类型断言,完全安全)
把两个函数参数打包为一个元组联合类型,利用TypeScript对可辨识联合的原生窄化能力实现内部类型推导:
// 原有类型定义无需修改 enum TYPE { TYPE1 = "type1", TYPE2 = "type2", }; type State1 = { property1: number, }; type State2 = { property1: string, property2: string, }; type Container1 = { type: TYPE.TYPE1, }; type Container2 = { type: TYPE.TYPE2, } // 定义参数元组的联合类型 type FnParams = | [c: Container1, r: State1] | [c: Container2, r: State2] // 函数直接使用联合类型作为入参 const fn = (...[c, r]: FnParams): number | string | null => { switch (c.type) { case TYPE.TYPE1: { // r自动被窄化为State1类型 return r.property1; } case TYPE.TYPE2: { // r自动被窄化为State2类型,可正常访问property2 console.log(r.property2) return r.property1; } default: { return null; } } } // 调用方式和类型推断完全不变 const result = fn({type: TYPE.TYPE1}, {property1: 1}); // result类型为number
方案2:使用函数重载(兼容原有参数结构)
如果不想改动参数的传入形式,可以用函数重载实现,仅需要在函数内部做少量安全的类型断言:
// 原有类型定义无需修改 // 函数重载定义 function fn(c: Container1, r: State1): number; function fn(c: Container2, r: State2): string; function fn(c: Container, r: State1 | State2): number | string | null { switch (c.type) { case TYPE.TYPE1: { const state = r as State1 return state.property1; } case TYPE.TYPE2: { const state = r as State2 // 可正常访问property2 console.log(state.property2) return state.property1; } default: { return null; } } } // 调用方式和类型推断完全不变 const result = fn({type: TYPE.TYPE1}, {property1: 1}); // result类型为number
方案选择建议
- 优先选择方案1:全程无类型断言,所有类型推导由编译器自动完成,不存在类型安全风险
- 如果需要兼容现有大量的函数调用代码,不想改动参数传入形式,可以选择方案2,只要内部逻辑保证类型断言和参数的对应关系正确即可
内容的提问来源于stack exchange,提问作者David Vass
相关产品推荐
相关产品推荐

