如何用TypeScript定义含description与数字类型ID属性的超类型?
TypeScript 定义含动态ID属性与固定description的超类型
问题场景
我们需要为一组键值结构的子类型定义超类型,后端返回的结构未使用key和value,而是采用somethingId+description的形式,示例如下:
{ somethingId: 1, description: "Entity description" }
其中仅something部分会随实体类型变化,要求该超类型必须满足:
- 包含一个string类型的
description属性 - 包含且仅包含一个number类型的动态命名属性(命名以
Id结尾)
原有尝试的问题
之前的定义方式无法满足严格类型约束:
// 错误:TypeScript会强制所有属性(包括description)为number类型 type MyType = { [k: string]: number; description: string; } // 不足:所有属性都允许是number或string,无法保证动态属性仅为number、且只有一个动态属性 type MyType = { [k: string]: number | string; description: string; }
解决方案
方案1:泛型指定动态属性名(推荐,类型最严格)
通过泛型明确动态ID属性的名称,既保证类型安全,又支持不同实体的ID命名:
// 泛型T指定动态ID属性的名称 type EntityType<T extends string> = { [K in T]: number; } & { description: string; }; // 针对不同实体创建子类型 type SomethingEntity = EntityType<'somethingId'>; type UserEntity = EntityType<'userId'>; // 合法示例 const validSomething: SomethingEntity = { somethingId: 123, description: "这是一个实体描述" }; // 非法示例(TypeScript会报错) // const invalidSomething: SomethingEntity = { // somethingId: "123", // 类型应为number // description: "错误示例" // };
方案2:匹配任意以Id结尾的属性(无需固定ID名称)
如果不需要指定具体的ID属性名,仅要求存在唯一的xxxId格式的number属性,可通过映射类型结合条件类型实现:
type IdEntity = { // 仅保留以Id结尾的属性,类型为number [K in string as K extends `${string}Id` ? K : never]: number; } & { description: string; } & { // 确保没有多余属性 [key: string]: never; }; // 合法示例 const validEntity: IdEntity = { orderId: 456, description: "订单描述" }; // 非法示例(TypeScript会报错) // const invalidEntity: IdEntity = { // orderId: 456, // userId: 789, // 存在多个Id属性 // description: "错误示例" // };
方案3:类型断言(临时兼容场景,不推荐)
如果需要快速兼容现有结构,可通过类型守卫做运行时检查,不过类型约束较弱:
type BaseEntity = { description: string; }; // 类型守卫:验证对象符合要求 function isEntity<T extends BaseEntity>(obj: T): obj is T & Record<string, number> { const nonDescKeys = Object.keys(obj).filter(key => key !== 'description'); return nonDescKeys.length === 1 && typeof obj[nonDescKeys[0] as keyof T] === 'number'; } // 使用示例 const testObj = { productId: 789, description: "产品描述" }; if (isEntity(testObj)) { // 此处testObj的类型被正确推断 console.log(testObj.productId, testObj.description); }
内容的提问来源于stack exchange,提问作者César Rodriguez
相关产品推荐
相关产品推荐

