TypeScript工厂函数返回可辨识联合类型报错如何解决?
问题原因
TypeScript 目前暂不支持在泛型函数内部,对依赖泛型参数的条件类型返回值做类型自动窄化。你代码里的DiscriminateUnion<Shape, 'kind', T>是依赖泛型T的条件类型,即使你通过switch分支判断了shapeKind的具体值,编译器也无法将泛型T和当前分支的具体字符串字面量类型做绑定,因此判定返回的Circle/Square实例和返回值类型不匹配,抛出以下错误:
Type 'Circle' is not assignable to type 'DiscriminateUnion<Circle, "kind", T> | DiscriminateUnion<Square, "kind", T>'.
Type 'Circle' is not assignable to type 'DiscriminateUnion<Square, "kind", T>'.(2322)
解决方案
以下是两种常见的修复方式:
方案1:使用函数重载(更推荐,类型提示更直观)
直接为不同入参声明对应的返回类型,复杂的泛型条件类型都可以省略:
type ShapeKind = 'circle' | 'square' class Circle{ kind: "circle" = 'circle'; radius: number; constructor() { this.radius = 4 } } class Square{ kind: "square" = 'square'; sideLength: number; constructor() { this.sideLength = 4 } } type Shape = Circle | Square; // 函数重载声明 function makeShape(shapeKind: 'circle'): Circle function makeShape(shapeKind: 'square'): Square // 函数实现 function makeShape(shapeKind: ShapeKind): Shape { switch (shapeKind) { case 'circle': return new Circle() case 'square': return new Square() default: throw Error('not valid shape') } } const newSquare = makeShape('square') // 编译器自动识别为Square类型 const newCircle = makeShape('circle') // 编译器自动识别为Circle类型
方案2:添加类型断言(改动最小)
你的业务逻辑本身是正确的,只是编译器无法自动推导类型匹配关系,所以可以手动给返回值加上类型断言,告知编译器类型符合预期:
type ShapeKind = 'circle' | 'square' class Circle{ kind: "circle" = 'circle'; radius: number; constructor() { this.radius = 4 } } class Square{ kind: "square" = 'square'; sideLength: number; constructor() { this.sideLength = 4 } } type Shape = Circle | Square; type DiscriminateUnion<T, K extends keyof T, V extends T[K]> = T extends Record<K, V> ? T : never function makeShape<T extends ShapeKind>(shapeKind: T): DiscriminateUnion<Shape, 'kind', T> { switch (shapeKind) { case 'circle': // 手动添加类型断言 return new Circle() as DiscriminateUnion<Shape, 'kind', T> case 'square': // 手动添加类型断言 return new Square() as DiscriminateUnion<Shape, 'kind', T> default: throw Error('not valid shape') } } const newSquare = makeShape('square')
内容的提问来源于stack exchange,提问作者ghclark2
相关产品推荐
相关产品推荐

