如何让TypeScript对递归判别式联合类型进行类型推断?
TypeScript递归判别式联合类型的类型推断问题解析
为什么当前代码无法推断类型?
你当前的getItem函数返回类型被隐式推断为AnyItem(即Circle | Square | Bag),TypeScript无法跟踪传入的构造参数的递归嵌套结构:
- 普通的类型守卫只能处理当前层级的类型判断,没法把输入参数的嵌套类型信息传递到返回值的嵌套结构中;
- 函数返回的是顶层联合类型,TypeScript不知道某个具体的
Bag构造参数对应的children里的每个元素会生成什么类型的Item,只能默认是AnyItem联合类型。
正确实现方案:泛型+条件类型映射
通过定义泛型类型来关联构造参数和最终Item的类型,让TypeScript能递归推断嵌套结构的类型:
步骤1:定义构造参数到Item的映射类型
首先创建一个泛型类型,根据构造参数的type字段,精确映射到对应的Item类型,包括嵌套的children:
type ConstructedItem<T extends AnyConstructorParams> = T extends CircleConstructorParams ? Circle : T extends SquareConstructorParams ? Square : T extends BagConstructorParams ? Bag & { children: ConstructedItem<T['children'][number]>[] } : never;
这个类型会递归处理Bag的children,把每个子构造参数映射为对应的Item类型。
步骤2:修改getItem为泛型函数
让函数接收泛型参数,返回对应的ConstructedItem<T>类型:
function getItem<T extends AnyConstructorParams>(itemConstructorParams: T): ConstructedItem<T> { if (itemConstructorParams.type === 'circle') { return { type: 'circle', radius: 3, } as ConstructedItem<T>; } else if (itemConstructorParams.type === 'square') { return { type: 'square', sides: 4, } as ConstructedItem<T>; } else if (itemConstructorParams.type === 'bag') { return { type: 'bag', color: 'green', children: itemConstructorParams.children.map(child => getItem(child)) } as ConstructedItem<T>; } throw new Error('Invalid'); }
这里的as ConstructedItem<T>是帮助TypeScript确认每个分支的返回值符合泛型映射的类型,属于辅助推导的断言,而非强制类型转换。
验证效果
现在执行测试代码,TypeScript可以精确推断每个嵌套Item的类型:
const circle = getItem({ type: 'circle' }); console.log(circle.radius); // 正确,类型为Circle const square = getItem({ type: 'square' }); console.log(square.sides); // 正确,类型为Square const nested = getItem({ type: 'bag', children: [ { type: 'square' }, { type: 'circle' }, { type: 'bag', children: [ { type: 'square' }, { type: 'circle' }, ] }, ], }); console.log(nested.children[2].children[1].radius); // 正确,TypeScript能推断出这是Circle类型
关键原理
通过泛型把输入参数的类型和输出Item的类型绑定,再用条件类型递归处理嵌套结构,让TypeScript能沿着构造参数的嵌套层级,一步步推导出每个Item的精确类型,而不是退化为顶层的联合类型。
内容的提问来源于stack exchange,提问作者kohloth
相关产品推荐
相关产品推荐

