如何实现允许基类赋值但禁止同基类其他标称类型赋值的LooseNominal类型?
实现允许基类型赋值但禁止同基类型其他标称类型赋值的LooseNominal
我需要创建一种“松散”标称类型LooseNominal,它允许从基类型直接赋值,但禁止从拥有同一基类型的其他标称类型赋值。现有一个严格标称类型的实现:
declare const nominalSymbol: unique symbol; export type Nominal<T extends string, U> = U & { [nominalSymbol]: T };
我尝试过将LooseNominal定义为Nominal<T, U> | U,但这样会导致不同的LooseNominal类型之间因为共享基类型而可以互相赋值,不符合需求。期望的行为如下:
type Address = LooseNominal<'Address', string>; type FirstName = LooseNominal<'FirstName', string>; let address: Address = "123 Some St"; let firstName: FirstName = "John"; address = "456 Another St"; // 无问题 address = firstName; // 期望触发类型错误!
解决方案
可以利用可选的唯一符号属性来实现需求。核心思路是让LooseNominal类型包含基类型,同时附加一个可选的符号属性用于区分不同的标称类型。普通基类型值因为没有这个属性可以直接赋值,而其他标称类型因为带有属性值不匹配的符号属性会被TypeScript拦截。
实现代码如下:
declare const nominalSymbol: unique symbol; // 保留原有的严格标称类型 export type Nominal<T extends string, U> = U & { [nominalSymbol]: T }; // 实现松散标称类型 export type LooseNominal<T extends string, U> = U & { [nominalSymbol]?: T };
测试验证
type Address = LooseNominal<'Address', string>; type FirstName = LooseNominal<'FirstName', string>; let address: Address = "123 Some St"; // ✅ 正常赋值 let firstName: FirstName = "John"; // ✅ 正常赋值 address = "456 Another St"; // ✅ 基类型值可正常赋值 address = firstName; // ❌ 类型错误:类型"FirstName"不能赋值给类型"Address",属性"[nominalSymbol]"的类型不兼容
原理说明
- 普通的基类型值(如
string)没有nominalSymbol属性,而LooseNominal中的该属性是可选的,因此可以直接赋值。 - 不同的
LooseNominal类型带有不同的符号属性值('Address'vs'FirstName'),当互相赋值时,TypeScript会检查属性类型的兼容性,因为属性值不匹配而触发类型错误。 - 如果需要将严格的
Nominal类型赋值给对应的LooseNominal类型,也是可行的,因为Nominal<T, U>是LooseNominal<T, U>的子类型(必填属性是可选属性的子类型)。
内容的提问来源于stack exchange,提问作者user3534080
相关产品推荐
相关产品推荐

