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

泛型无法实现类型收窄:如何限制参数为同一具体类型

问题:实现参数必须为同一类型的equal函数

我通过类型约束数据的可能表示,涉及的值类型为Foo或Bar(均为string的别名),即所有值属于Foo | Bar类型。希望编写一个equal函数,要求两个参数必须为同一类型,若传入不同类型参数则编译失败。

尝试了以下代码,但它能正常编译,不符合预期:

type Foo = string;
type Bar = string;
type FooBar = Foo | Bar;

function equal<T extends FooBar>(n1: T, n2: T): boolean {
  return n1 === n2;
}

const n1: Foo = "foo";
const n2: Bar = "bar";

equal(n1, n2); // 期望编译失败,但实际通过

推测是类型拓宽导致编译器将两者视为FooBar宽类型,请问如何指定泛型T只能是Foo或Bar,而非FooBar?


原因分析

TypeScript采用结构类型系统,Foo和Bar都是string的别名,本质属于同一类型,编译器无法从语义上区分它们。泛型T会被自动推断为Foo | Bar(即string),自然不会触发编译错误。

要实现需求,必须让Foo和Bar成为语义独立的类型,可通过以下两种方式实现:

方案1:使用字面量类型(适用于固定值场景)

如果Foo和Bar对应固定的字符串字面量,直接定义为字面量类型即可:

type Foo = "foo";
type Bar = "bar";

function equal<T extends Foo | Bar>(n1: T, n2: T): boolean {
  return n1 === n2;
}

const n1: Foo = "foo";
const n2: Bar = "bar";

equal(n1, n2); // 编译失败:类型"bar"的参数不能赋给类型"foo"的参数
equal(n1, n1); // 编译通过
equal(n2, n2); // 编译通过

方案2:使用品牌类型(Branded Types,适用于任意字符串场景)

如果Foo和Bar是任意字符串,但需要语义区分,可以给类型添加唯一的品牌标记:

type Foo = string & { __brand: "Foo" };
type Bar = string & { __brand: "Bar" };

// 辅助函数用于创建带品牌的类型实例
function createFoo(value: string): Foo {
  return value as Foo;
}

function createBar(value: string): Bar {
  return value as Bar;
}

function equal<T extends Foo | Bar>(n1: T, n2: T): boolean {
  return n1 === n2;
}

const n1 = createFoo("foo");
const n2 = createBar("bar");

equal(n1, n2); // 编译失败:类型"Bar"的参数不能赋给类型"Foo"的参数
equal(n1, createFoo("anotherFoo")); // 编译通过
equal(n2, createBar("anotherBar")); // 编译通过

品牌类型通过交叉类型给string添加一个虚构的唯一属性(__brand),让TypeScript认为Foo和Bar是不同类型,但运行时该属性不存在,不会影响代码执行逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:12:18