TypeScript运行时提取对象子类型属性及getKeysOf函数实现咨询
TypeScript运行时提取指定类型属性子集实现方案
核心前提
TypeScript 的类型信息仅存在于编译阶段,运行时会被完全擦除,因此你示例中直接通过泛型参数调用getKeysOf<OUT>()的写法无法通过原生TS实现,需要配合额外的编译时转换或者元数据标记方案。
可行实现方案
方案1:使用TypeScript自定义转换器
社区提供的ts-transformer-keys类转换器可以在编译阶段将指定类型的属性键转换为运行时可用的数组,不需要额外的运行时代码标记:
import { keys } from 'ts-transformer-keys'; function only<IN extends OUT, OUT>(object: IN, outKeys: Array<keyof OUT>): OUT { const result = {} as OUT; for (const key of outKeys) { result[key] = object[key]; } return result; } // 示例 interface Foo { x: string; } interface Bar extends Foo { y: string; } const foo = only<Bar, Foo>({x: 'x', y: 'y'}, keys<Foo>()); // 输出: {x: 'x'}
该方案的keys<Foo>()会在编译阶段直接被替换为['x']字面量数组,运行时可直接使用。
方案2:装饰器+反射元数据实现
如果你不想引入额外的TS编译转换器,可以通过reflect-metadata库配合装饰器实现,将类型属性信息存入运行时元数据:
首先在tsconfig.json中开启装饰器和元数据输出配置:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
实现代码:
import 'reflect-metadata'; const PROP_KEYS_META = 'extractable_property_keys'; // 标记可提取属性的类 function Extractable() { return (target: any) => { Reflect.defineMetadata(PROP_KEYS_META, [], target); }; } // 标记需要提取的属性 function Expose() { return (target: any, propKey: string) => { const keys = Reflect.getMetadata(PROP_KEYS_META, target.constructor) || []; keys.push(propKey); Reflect.defineMetadata(PROP_KEYS_META, keys, target.constructor); }; } // 从类元数据中获取属性列表 function getKeysOf<T>(clazz: new () => T): Array<keyof T> { return Reflect.getMetadata(PROP_KEYS_META, clazz) || []; } // 实现only泛型函数 function only<IN extends OUT, OUT>(object: IN, outClazz: new () => OUT): OUT { const result = {} as OUT; for (const key of getKeysOf(outClazz)) { result[key] = object[key]; } return result; } // 示例 @Extractable() class Foo { @Expose() x: string; } class Bar extends Foo { y: string; } const foo = only<Bar, Foo>({x: 'x', y: 'y'}, Foo); // 输出: {x: 'x'}
简化无依赖方案
如果你的场景不需要通用能力,也可以直接手动传入属性列表,不需要额外依赖或编译配置,TS会自动校验属性的合法性:
function only<IN extends OUT, OUT>(object: IN, keys: Array<keyof OUT>): OUT { return Object.fromEntries( keys.map(key => [key, object[key]]) ) as OUT; } // 示例 interface Foo { x: string; } interface Bar extends Foo { y: string; } const foo = only<Bar, Foo>({x: 'x', y: 'y'}, ['x']); // 输出: {x: 'x'}
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

