如何区分TypeScript中UserId与CustomerId两种字符串类型ID?
解决TypeScript中字符串类型ID互相兼容的问题
TypeScript的类型别名采用结构类型系统,仅基于string的类型别名会被视为完全兼容,导致不同业务含义的ID可以互相赋值。以下是几种可行的解决方案:
方案一:品牌类型(Tagged Types)
通过给类型添加唯一的虚拟“品牌”属性,让TypeScript识别为不同类型,同时不影响运行时性能。
// 定义带品牌标识的ID类型 export type CustomerId = string & { __brand: 'CustomerId' }; export type UserId = string & { __brand: 'UserId' }; // 工厂函数封装类型断言,避免手动重复编写 export const createCustomerId = (id: string): CustomerId => id as CustomerId; export const createUserId = (id: string): UserId => id as UserId; // Customer类定义 export class Customer { id: CustomerId; userId: UserId; constructor(idStr: string, userIdStr: string) { this.id = createCustomerId(idStr); this.userId = createUserId(userIdStr); } } // 测试代码 class Test { foo() { const userId = createUserId("abc123"); // 此时编译器会抛出错误,符合预期 this.bar(userId); // 类型错误:无法将类型“UserId”分配给类型“CustomerId” } bar(customerId: CustomerId) {} }
方案二:类封装(运行时可区分)
用类包裹字符串值,让每个ID成为类的实例,TypeScript会自动区分类型,同时运行时也能明确识别ID类型。
export class CustomerId { constructor(public value: string) {} } export class UserId { constructor(public value: string) {} } export class Customer { id: CustomerId; userId: UserId; constructor(idStr: string, userIdStr: string) { this.id = new CustomerId(idStr); this.userId = new UserId(userIdStr); } } // 测试代码 class Test { foo() { const userId = new UserId("abc123"); // 编译器报错,符合预期 this.bar(userId); // 类型错误:无法将类型“UserId”分配给类型“CustomerId” } bar(customerId: CustomerId) {} }
方案三:TypeScript 5.0+ 内置品牌类型
如果使用TypeScript 5.0及以上版本,可以使用官方内置的brand关键字,语法更简洁:
export type CustomerId = string & brand "CustomerId"; export type UserId = string & brand "UserId"; // 创建ID实例 const customerId = "customer-uuid-123" as CustomerId; const userId = "user-uuid-456" as UserId; // 测试代码 function bar(customerId: CustomerId) {} bar(userId); // 编译器抛出类型错误
内容的提问来源于stack exchange,提问作者Arnaud Denoyelle
相关产品推荐
相关产品推荐

