面试题答案
一键面试- 使用接口和类型别名定义名义类型:
- 对于
OrderId
,可以这样定义:
type OrderId = string & { readonly __orderIdBrand: unique symbol };
- 对于
PermissionId
,定义为:
type PermissionId = number & { readonly __permissionIdBrand: unique symbol };
- 通过这种方式,每个名义类型都有自己独特的“品牌”,即使它们底层的数据类型相同,TypeScript 也能区分它们。
- 对于
- 封装操作函数:
- 为每个名义类型创建专门的操作函数,例如对于
OrderId
:
function createOrderId(id: string): OrderId { return id as OrderId; } function getOrderIdValue(id: OrderId): string { return id; }
- 对于
PermissionId
类似:
function createPermissionId(id: number): PermissionId { return id as PermissionId; } function getPermissionIdValue(id: PermissionId): number { return id; }
- 这样在不同模块中操作这些名义类型时,都通过这些封装的函数,保证类型的正确使用。
- 为每个名义类型创建专门的操作函数,例如对于
- 模块间数据传递:
- 在模块间传递数据时,严格按照定义的名义类型进行参数和返回值的类型标注。例如,如果一个函数接收
OrderId
:
function processOrder(orderId: OrderId) { // 处理订单逻辑 }
- 调用该函数时,确保传入的是
OrderId
类型,而不是普通的string
。这样可以避免类型混淆。
- 在模块间传递数据时,严格按照定义的名义类型进行参数和返回值的类型标注。例如,如果一个函数接收
- 使用泛型增强复用性:
- 如果有一些通用的逻辑适用于不同的名义类型,可以使用泛型来实现复用。例如,假设有一个缓存函数:
type NominalType<T> = T & { readonly __brand: unique symbol }; const cache: Record<string, any> = {}; function cacheValue<T extends NominalType<any>>(key: string, value: T): T { if (!cache[key]) { cache[key] = value; } return cache[key] as T; }
- 这个函数可以用于缓存不同的名义类型的值,同时保持类型安全性。