如何阻止无鉴别器的重叠可选TypeScript类型互用?
问题描述
我有两个TypeScript类型,它们存在重叠属性但可选属性不同,目前可以互换使用。请问有没有办法阻止这种情况,让它们无法互换?我知道鉴别器属性的概念,但没办法给接口添加额外属性。
示例代码:
interface A { unique1?: string; common?: string; // 可选或必选都可以 } interface B { unique2?: string; common?: string; // 可选或必选都可以 } const a: A = { unique1: 'a', common: 'a', } // 没有报错 const b: B = a; function foo(b: B) { // 无操作 } // 没有报错 foo(a);
解决方案:使用品牌类型(Branded Types)
可以通过给类型添加一个仅用于类型区分的私有属性(不需要在运行时实际存在),让TypeScript将它们视为不同类型,无需修改原接口的实际属性。
实现方式一:通用品牌辅助类型
// 定义通用的品牌类型辅助工具 type Brand<T, BrandId> = T & { readonly [Symbol.toStringTag]: BrandId }; // 基于原接口创建带唯一品牌标识的类型 type BrandedA = Brand<A, "A">; type BrandedB = Brand<B, "B">; // 创建实例时通过类型断言标记品牌 const a: BrandedA = { unique1: 'a', common: 'a', } as BrandedA; // ❌ 报错:类型 "BrandedA" 无法赋值给类型 "BrandedB" const b: BrandedB = a; function foo(b: BrandedB) {} // ❌ 报错:类型 "BrandedA" 的参数无法赋给类型 "BrandedB" 的参数 foo(a);
实现方式二:直接添加品牌属性
如果不需要通用工具,也可以直接给类型添加唯一的私有属性:
type BrandedA = A & { readonly __brand: "A" }; type BrandedB = B & { readonly __brand: "B" }; const a: BrandedA = { unique1: 'a', common: 'a', } as BrandedA; // ❌ 报错:类型不兼容 const b: BrandedB = a;
原理说明
- 品牌类型通过交叉类型给原类型附加一个只读的、唯一标识的属性,这个属性仅存在于类型层面,运行时不需要实际赋值。
- TypeScript会根据这个唯一标识判断类型是否兼容,从而阻止原本可以互换的类型被错误赋值。
- 使用
Symbol.toStringTag或__brand这类命名,既能避免和业务属性冲突,也符合TypeScript社区的约定。
内容的提问来源于stack exchange,提问作者Mitchell Loeppky
相关产品推荐
相关产品推荐

