如何在NestJS的PickType中重命名选中的属性
扩展NestJS的PickType以支持属性重命名
我需要改造NestJS的PickType工具类型,使其支持在选取属性时对指定属性进行重命名,同时满足以下规则:
- 规则1:如果重命名后的属性名在原类中已存在,且该属性也被选中,则抛出类型错误
- 规则2:如果重命名后的属性名在原类中已存在,但未被选中,则正常生成重命名后的属性
原有PickType定义回顾
原PickType仅支持选取原类的属性,无法重命名:
interface Type<T = any> extends Function { new (...args: any[]): T; } declare function PickType<T, K extends keyof T>(classRef: Type<T>, keys: readonly K[]): Type<Pick<T, typeof keys[number]>>; class AClazz { a: string; b: string; c: number; } const picked = PickType(AClazz, [ 'a', 'c', ] as const); // picked 的类型为 { a: string; c: number }
改造后的PickType实现
通过类型编程实现重命名支持与冲突校验,完整类型声明如下:
interface Type<T = any> extends Function { new (...args: any[]): T; } // 提取每个元素的原属性键(类型断言前的真实键) type ExtractOriginalKey<T> = T extends `${infer K}` ? K : never; // 提取每个元素的目标属性键(类型断言后的重命名键) type ExtractTargetKey<T> = T extends infer U ? U : never; // 校验重命名是否与已选中的原属性冲突 type ValidateRenameConflict< T, Keys extends readonly any[], OriginalKeys = ExtractOriginalKey<Keys[number]>, TargetKeys = ExtractTargetKey<Keys[number]> > = Exclude<TargetKeys, OriginalKeys> extends infer RenamedKeys ? RenamedKeys extends keyof T ? RenamedKeys extends OriginalKeys ? never // 重命名后的键是原类中已被选中的属性,触发类型错误 : unknown : unknown : unknown; // 支持重命名的PickType declare function PickType< T, Keys extends readonly any[], _ConflictCheck extends ValidateRenameConflict<T, Keys> = ValidateRenameConflict<T, Keys> >( classRef: Type<T>, keys: Keys & readonly (keyof T | `${keyof T}`)[] ): Type<{ [K in Keys[number] as ExtractTargetKey<K>]: T[ExtractOriginalKey<K>] }>;
示例验证
基础重命名场景
将原类的c属性重命名为d,生成预期类型:
class AClazz { a: string; b: string; c: number; } const picked = PickType(AClazz, [ 'a', 'c' as 'd', ] as const); // picked 的类型为 { a: string; d: number }
规则1:重命名后属性已被选中,触发错误
当重命名后的属性d同时被作为原属性选中时,类型校验报错:
class AClazz { a: string; b: string; c: number; d: string; } // 类型错误:'d'已存在且被选中 const picked = PickType(AClazz, [ 'a', 'c' as 'd', 'd', ] as const);
规则2:重命名后属性存在但未被选中,正常生成
重命名后的属性d在原类存在但未被选中时,正常生成目标类型:
class AClazz { a: string; b: string; c: number; d: string; } const picked = PickType(AClazz, [ 'a', 'c' as 'd', ] as const); // picked 的类型为 { a: string; d: number }
内容的提问来源于stack exchange,提问作者user1373316
相关产品推荐
相关产品推荐

