如何让TypeScript的Object.keys类型工具排除类实例不可枚举方法
我明白你的问题了——你写的objectKeys函数在处理类实例时,TypeScript类型推断会返回类的方法名,但实际运行时Object.keys因为这些方法在原型链上(默认不可枚举),所以返回空数组,这就造成了类型和实际值的不一致。
问题根源
TypeScript的keyof T会包含对象的所有可访问属性,包括原型链上的方法,但Object.keys只返回对象自身的可枚举字符串键。对于类实例来说,类中定义的方法是挂载到原型上的(默认不可枚举),所以不会被Object.keys捕获,但keyof T却会把这些方法名包含进来,导致你的类型推断和实际运行结果不匹配。
解决方案
我们可以修改ObjectKeys类型,让它排除原型链上的属性,只保留对象自身的可枚举属性键。核心思路是:利用类实例的constructor属性获取类的原型类型,排除原型上的所有方法名;对于普通对象字面量,则排除Object.prototype上的内置方法(这符合Object.keys的行为,因为内置方法默认不可枚举)。
修改后的代码如下:
type ObjectKeys<T> = T extends object ? T extends { constructor: infer C } ? C extends new (...args: any[]) => any ? Exclude<keyof T, keyof C['prototype']> : Extract<keyof T, string> : Extract<keyof T, string> : never; function objectKeys<T>(o: T) { if (o === undefined || o === null) { return []; } return Object.keys(o) as ReadonlyArray<ObjectKeys<T>>; }
测试验证
用你的示例和扩展场景测试:
const o1 = { f: () => 1 }; const keys3 = objectKeys(o1); // 类型为ReadonlyArray<'f'>,实际值为['f'],完全匹配 class C { f() {} } const c1 = new C(); const keys4 = objectKeys(c1); // 类型为ReadonlyArray<never>(即readonly []),实际值为[],完全匹配 class D { x = 1; f() {} } const d1 = new D(); const keys5 = objectKeys(d1); // 类型为ReadonlyArray<'x'>,实际值为['x'],完全匹配
注意事项
如果你的对象字面量覆盖了Object.prototype上的内置方法(比如toString),这个类型会排除该方法名,但实际Object.keys会返回它(因为它是自身可枚举属性)。这是TypeScript类型系统的局限性——无法区分“自身覆盖的原型方法”和“原型原生方法”,但这种场景相对少见,遇到时可以通过类型断言修正,或者进一步调整类型(会增加复杂度)。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

