You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:38:21