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

如何在TypeScript泛型函数中正确处理联合类型?

解决方案:TypeScript泛型约束与联合类型匹配问题

问题背景

先看这段示例代码:

type AB = {
    foo: "A"
    bar: "A"
} | {
    foo: "B"
    bar: "B"
}

type Foo = AB["foo"]

function fun<F extends Foo>(foo: F, bar: Extract<AB, { foo: F }>["bar"]) {
    fun2({
        foo,
        bar,
    })
}

function fun2(ab: AB) { }

运行时会抛出错误:

类型为'{ foo: "A" | "B"; bar: "A" | "B"; }'的参数无法赋值给类型'AB'的参数。

接下来针对两个核心问题给出解决方案:


问题1:如何限制泛型类型不接受联合类型?

可以利用TypeScript联合类型的分配特性,构造条件类型约束来阻止泛型接收联合类型:

type NoUnion<T> = [T] extends [UnionToIntersection<T>] ? T : never;

核心逻辑:如果T是联合类型,UnionToIntersection<T>会将其转为交叉类型,此时[T]与[交叉类型]不匹配,返回never;如果T是单个类型,两者匹配,返回T。

修改后的泛型函数:

function fun<F extends NoUnion<Foo>>(foo: F, bar: Extract<AB, { foo: F }>["bar"]) {
    fun2({ foo, bar });
}

这样当传入联合类型作为F时,TypeScript会直接报错,确保泛型仅接收单个类型,无需枚举所有类型或使用不推荐的联合转元组方案。


问题2:如何避免bar: Extract<AB, { foo: F }>["bar"]返回联合类型?

核心原因

当泛型F是联合类型时,Extract<AB, { foo: F }>会因分配性遍历联合成员,最终返回AB本身,导致bar的类型变为"A" | "B";而直接写Extract<AB, { foo: "A" }>时,foo是具体字面量,自然返回对应单类型的bar。

另外,指定fun<F extends "A">时,TypeScript会保留泛型参数F的标识(即便F实际等于"A"),不会自动将其收窄为字面量"A",导致Extract操作无法精准匹配,最终返回联合类型。

解决方案

通过泛型条件绑定,让bar类型与foo严格关联:

// 方式一:显式分支判断
function fun<F extends Foo>(foo: F, bar: F extends "A" ? "A" : "B") {
    fun2({ foo, bar });
}

// 方式二:通用类型提取
type BarForFoo<F extends Foo> = AB extends { foo: F; bar: infer B } ? B : never;
function fun<F extends Foo>(foo: F, bar: BarForFoo<F>) {
    fun2({ foo, bar });
}

两种方式都能确保bar的类型与foo严格一致,不会返回联合类型。

针对附带疑问:指定fun<F extends "A">时显示foo: F而非foo: "A",是因为TypeScript会保留泛型参数的标识,除非使用as const或显式断言,否则不会自动将F替换为具体字面量"A"。


内容的提问来源于stack exchange,提问作者TN.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 18:53:24