TypeScript:为动态生成的类型添加额外属性
问题描述
现有以下TypeScript类型定义:
export enum Category { car = 'car', truck = 'truck', } export interface Property { readonly name: string, readonly label: string, readonly type: 'number' | 'string', } export interface CategoryDefinition { readonly category: Category, readonly label: string, readonly properties: readonly Property[] } type TypeMap = { string: string; number: number; }; type FromDefinition<D extends CategoryDefinition> = { [T in D['properties'][number] as T['name']]: TypeMap[T['type']]; };
通过如下定义生成Car类型:
const car = { category: Category.car, label: 'Car', properties: [ { name: 'weight', label: 'Weight', type: 'number', } ], } as const satisfies CategoryDefinition; export type Car = FromDefinition<typeof car>;
当前生成的类型是:
type Car = { weight: number; }
现在需要扩展FromDefinition类型,让生成的类型包含category属性,期望结果为:
type Car = { category: 'car'; weight: number; }
尝试过交叉类型的方式:
export type CategoryExtension= { category: Category }; type FromDefinition<D extends CategoryDefinition> = { [T in D['properties'][number] as T['name']]: TypeMap[T['type']]; } & CategoryExtension;
但生成的类型显示不够直观,会以交叉类型的形式呈现:
type Car = { weight: number; } & CategoryExtension
需要让生成的类型直接展开为包含category属性的对象类型,而非交叉类型形式。
解决方案
方案1:直接合并属性到映射类型
可以直接在FromDefinition中把category属性和映射出的属性合并,利用泛型参数D的具体类型推导category的精确类型:
type FromDefinition<D extends CategoryDefinition> = { category: D['category']; } & { [T in D['properties'][number] as T['name']]: TypeMap[T['type']]; };
方案2:使用工具类型优化显示
如果编辑器仍然显示为交叉类型,可以借助Prettify工具类型强制展开为单一对象类型:
// 工具类型:展开交叉类型为单一对象 type Prettify<T> = { [K in keyof T]: T[K]; } & {}; type FromDefinition<D extends CategoryDefinition> = Prettify<{ [T in D['properties'][number] as T['name']]: TypeMap[T['type']]; } & { category: D['category']; }>;
最终生成的Car类型会显示为:
type Car = { category: 'car'; weight: number; }
注:由于你已经给car常量添加了as const断言,D['category']会被推导为枚举成员的字面量值,编辑器会自动显示为'car'而非Category.car。
内容的提问来源于stack exchange,提问作者Hafnernuss
相关产品推荐
相关产品推荐

