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

如何在TypeScript中通过工厂函数将判别联合映射为具体类型(如类)?

TypeScript中createNode函数的类型匹配错误解决

你写的代码里,createNode函数的switch分支返回实例时出现类型报错,这不是TypeScript的bug,而是泛型参数在分支窄化时的类型匹配限制导致的。

先看你的原代码:

type Node =
    | { type: 'Group'; children: Node[] }
    | { type: 'Obj'; payload: number };

class Group {
    readonly type = 'Group';
    constructor(public n: Node[]) { }
}

class Obj {
    readonly type = 'Obj';
    constructor(public p: number) { }
}

type NodeMappings =
    { Group: Group; Obj: Obj };

function createNode<T extends Node>(node: T): NodeMappings[T['type']] {
    switch (node.type) {
        case 'Group':
            return new Group(node.children); // 类型错误
        case 'Obj':
            return new Obj(node.payload); // 类型错误
    }
}

问题原因

TypeScript在处理泛型函数的分支窄化时,没办法自动把泛型参数T同步窄化到对应的Node联合成员。虽然switch里检查node.type能把node本身的类型窄化,但编译器无法确认返回的实例类型就是NodeMappings[T['type']],因此抛出错误。

解决方案

方案一:添加类型断言

最简单的方式是给每个分支的返回值加上类型断言,明确告诉编译器返回值符合预期:

function createNode<T extends Node>(node: T): NodeMappings[T['type']] {
    switch (node.type) {
        case 'Group':
            return new Group(node.children) as NodeMappings[T['type']];
        case 'Obj':
            return new Obj(node.payload) as NodeMappings[T['type']];
    }
}

方案二:使用函数重载

用函数重载替代泛型,为每个节点类型定义明确的输入输出对应关系,编译器能精准匹配:

// 重载签名
function createNode(node: { type: 'Group'; children: Node[] }): Group;
function createNode(node: { type: 'Obj'; payload: number }): Obj;
// 实现签名
function createNode(node: Node): Group | Obj {
    switch (node.type) {
        case 'Group':
            return new Group(node.children);
        case 'Obj':
            return new Obj(node.payload);
    }
}

方案三:优化类型映射+自定义类型守卫

通过更严谨的类型定义和自定义类型守卫,实现更严格的类型检查:

type Node =
    | { type: 'Group'; children: Node[] }
    | { type: 'Obj'; payload: number };

class Group {
    readonly type = 'Group' as const; // 用const断言固定类型字面量
    constructor(public n: Node[]) { }
}

class Obj {
    readonly type = 'Obj' as const;
    constructor(public p: number) { }
}

type NodeMappings = {
    'Group': Group;
    'Obj': Obj;
};

// 自定义类型守卫,用于窄化node类型
function isNodeType<T extends keyof NodeMappings>(
    node: Node, type: T
): node is Extract<Node, { type: T }> {
    return node.type === type;
}

function createNode<T extends keyof NodeMappings>(node: Extract<Node, { type: T }>): NodeMappings[T] {
    if (isNodeType(node, 'Group')) {
        return new Group(node.children);
    } else if (isNodeType(node, 'Obj')) {
        return new Obj(node.payload);
    }
    throw new Error('未知节点类型');
}

以上三种方法都能解决类型错误问题,你可以根据代码场景选择:类型断言最快但需要自己保证类型安全;函数重载更直观易懂;第三种方案则适合需要严格类型校验的场景。

内容的提问来源于stack exchange,提问作者Philipp Mildenberger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 18:03:36