You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript条件返回类型:单条件正常多条件报错求因

可辨识联合工厂函数新增类型后类型不兼容问题

我实现了一个基于可辨识联合参数的形状工厂函数,代码如下:

interface CircleArgs { type: "circle", radius: number };
interface SquareArgs { type: "square", length: number };

type ShapeArgs = CircleArgs | SquareArgs;

class Circle { constructor(_: CircleArgs) {}}
class Square { constructor(_: SquareArgs) {}}

type Shape = Circle | Square;

type ShapeOfArgs<Args extends ShapeArgs> = 
   Args extends CircleArgs? Circle :
   Square;

function createShape<Args extends ShapeArgs>(args: Args): ShapeOfArgs<Args> {
    switch (args.type) {
        case "circle": return new Circle(args);
        case "square": return new Square(args);
    }
}

这个实现能让TypeScript根据参数正确推断返回类型:

const circle1 = createShape({type: "circle", radius: 1}); // 推断为Circle
const square1 = createShape({type: "square", length: 1}); // 推断为Square

同时在包装函数中也能正确传递返回类型:

class Container { insert(_: Shape): void {}; }

function createShapeIn<Args extends ShapeArgs>(args: Args, cont: Container) {
    const shape = createShape(args);
    cont.insert(shape);

    return shape;
}

const cont = new Container();
const circle2 = createShapeIn({type: "circle", radius: 1}, cont); // 仍推断为Circle
const square2 = createShapeIn({type: "square", length: 1}, cont); // 仍推断为Square

但新增一种形状类型后,代码无法编译:

新增Figure类型:

interface FigureArgs { type: "figure", amount: number };
class Figure { constructor(_: FigureArgs) {}}

更新联合类型:

type ShapeArgs = CircleArgs | SquareArgs | FigureArgs;
type Shape = Circle | Square | Figure;

修改条件返回类型:

type ShapeOfArgs<Args extends ShapeArgs> = 
    Args extends CircleArgs? Circle :
    Args extends SquareArgs? Square :
    Figure;

此时工厂函数出现类型不兼容报错:

function createShape<Args extends ShapeArgs>(args: Args): ShapeOfArgs<Args> {
    switch (args.type) {
        case "circle": return new Circle(args); // 错误:Circle无法赋值给ShapeOfArgs<Args>
        case "square": return new Square(args); // 错误:Square无法赋值给ShapeOfArgs<Args>
        case "figure": return new Figure(args); // 错误:Figure无法赋值给ShapeOfArgs<Args>
    }
}

请问我哪里出错了?

注:我知道可以用as断言强制通过,但这违背了类型检查的初衷。


问题原因

当你使用泛型Args extends ShapeArgs时,TypeScript无法在switch的每个分支中确定ShapeOfArgs<Args>的具体类型。因为Args是一个泛型参数,它可以是ShapeArgs联合中的任意成员,甚至是整个联合类型本身。TypeScript无法在分支中缩小泛型参数的范围,导致它无法验证new Circle(args)确实符合ShapeOfArgs<Args>的类型约束。

解决方案

方案1:利用可辨识联合自动推断(最简单)

不需要泛型和条件类型,直接让函数参数为ShapeArgs联合类型,TypeScript会自动根据type字段推断返回类型:

function createShape(args: ShapeArgs): Shape {
    switch (args.type) {
        case "circle": return new Circle(args);
        case "square": return new Square(args);
        case "figure": return new Figure(args);
    }
}

方案2:函数重载保留精确类型推断

如果需要让调用者得到具体的Circle/Square/Figure类型而非Shape联合,可以用函数重载定义每个分支的输入输出关系:

function createShape(args: CircleArgs): Circle;
function createShape(args: SquareArgs): Square;
function createShape(args: FigureArgs): Figure;
function createShape(args: ShapeArgs): Shape {
    switch (args.type) {
        case "circle": return new Circle(args);
        case "square": return new Square(args);
        case "figure": return new Figure(args);
    }
}

方案3:映射类型维护类型关联

用映射类型统一管理参数和形状的对应关系,避免手动编写条件类型,同时保留泛型的灵活性:

type ShapeMap = {
    circle: Circle;
    square: Square;
    figure: Figure;
};

type ShapeArgs = {
    [K in keyof ShapeMap]: { type: K } & (
        K extends "circle" ? { radius: number } :
        K extends "square" ? { length: number } :
        { amount: number }
    );
}[keyof ShapeMap];

function createShape<K extends keyof ShapeMap>(args: Extract<ShapeArgs, { type: K }>): ShapeMap[K] {
    switch (args.type) {
        case "circle": return new Circle(args) as ShapeMap[K];
        case "square": return new Square(args) as ShapeMap[K];
        case "figure": return new Figure(args) as ShapeMap[K];
    }
}

这里的as断言是安全的,因为我们已经通过Extract和switch分支确保了类型匹配,不会违背类型检查的初衷。


内容的提问来源于stack exchange,提问作者Levi Haskell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:25:23