本文介绍在 typescript 中如何让泛型函数根据输入对象的 `type` 字段(判别联合)自动推导并返回精确匹配的输出类型,解决 `switch` 分支内类型无法被正确收窄的核心问题。
在 TypeSc

最简洁、可维护且兼容性强的解法是 分离类型推导与运行时逻辑:
type CircleV1 = { type: "circle"; colour: string };
type CircleV2 = { type: "circle"; colour: string; sides: 1 };
type SquareV1 = { type: "square"; colour: string };
type SquareV2 = { type: "square"; colour: string; sides: 4 };
type ShapeV1 = CircleV1 | SquareV1;
type ShapeV2 = CircleV2 | SquareV2;
// ? 核心:显式、可读的类型映射
type ConvertedToV2 =
T["type"] extends "circle" ? CircleV2 :
T["type"] extends "square" ? SquareV2 :
never;
function convertToV2(v1Shape: T): ConvertedToV2 {
// ✅ 辅助函数:在宽泛类型上做完整判别,无泛型干扰
function convertToV2_helper(shape: ShapeV1): ShapeV2 {
switch (shape.type) {
case "circle":
return { ...shape, sides: 1 }; // ✅ 类型检查通过:CircleV1 → CircleV2
case "square":
return { ...shape, sides: 4 }; // ✅ 类型检查通过:SquareV1 → SquareV2
}
}
// ✅ 安全断言:映射逻辑与实现逻辑严格一致
return convertToV2_helper(v1Shape) as ConvertedToV2;
}
// ✅ 调用端获得完美类型推导
const circleV2 = convertToV2({ type: "circle", colour: "red" });
// ^? CircleV2 —— 具备智能提示、不可写 `sides: 4`
const squareV2 = convertToV2({ type: "square", colour: "blue" });
// ^? SquareV2 若项目使用 TypeScript 4.9+,函数重载是语义更清晰、无需断言的首选:
function convertToV2(shape: CircleV1): CircleV2;
function convertToV2(shape: SquareV1): SquareV2;
function convertToV2(shape: ShapeV1): ShapeV2 {
switch (shape.type) {
case "circle":
return { ...shape, sides: 1 } satisfies CircleV2; // ✅ satisfies 捕获构造错误
case "square":
return { ...shape, sides: 4 } satisfies SquareV2;
}
}satisfies 关键字能静态校验对象字面量是否恰好满足目标类型(而非仅是子类型),例如误写 type: "square" 在 circle 分支中会立即报错,大幅提升重构安全性。
通过上述任一方案,你都能实现「输入即契约,输出即承诺」的强类型迁移函数,在保持代码简洁的同时,获得 IDE 智能提示、编译期保障与无缝类型推导。