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

如何阻止无鉴别器的重叠可选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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 22:43:26