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

如何在TypeScript中无需传入实例存储类型参数的键?

问题:TypeScript泛型类中获取泛型参数的键并存储到Map中

我尝试编写如下TypeScript代码,希望在MyClass类中无需传入实例的情况下,获取泛型参数T的键并存储在Map中:

type TKeyed = { [key: string]: any };

class MyClass<T extends TKeyed> {
  private keysMap: Map<string, any>;

  constructor() {
    const keys = Object.keys({} as T); // 尝试获取T的键
    this.keysMap = new Map<string, any>();

    for (const key of keys) {
      this.keysMap.set(key, null); // 将键存入Map
    }
  }

  getKeysMap(): Map<string, any> {
    return this.keysMap;
  }
}

interface MyType extends TKeyed {
  prop1: string;
  prop2: number;
}

const myInstance = new MyClass<MyType>();
const keysMap = myInstance.getKeysMap();

console.log(keysMap); // 预期输出: Map { 'prop1' => null, 'prop2' => null }

预期输出是包含prop1和prop2的Map,但实际结果是Map (0) {}。我还尝试用const keys = Reflect.ownKeys({} as T);提取键,同样无效。请问该如何解决?


原因分析

TypeScript的类型系统是编译时生效的,泛型参数T在编译完成后会被完全擦除,运行时根本不存在这个类型信息。你写的{} as T只是编译时的类型断言,运行时它就是一个空对象,所以Object.keys和Reflect.ownKeys都只能拿到空数组,最终Map自然是空的。


解决方法

要在运行时获取泛型类型T的键,必须提供一个运行时存在的T类型对象作为参考(因为类型本身在运行时不存在)。这里有两种常用的实现方式:

方式一:构造函数传入样本对象

修改MyClass的构造函数,让它接收一个T类型的样本对象(值可以随便填,只要键正确就行):

type TKeyed = { [key: string]: any };

class MyClass<T extends TKeyed> {
  private keysMap: Map<string, any>;

  // 接收T类型的样本对象
  constructor(sample: T) {
    const keys = Object.keys(sample);
    this.keysMap = new Map<string, any>();

    for (const key of keys) {
      this.keysMap.set(key, null);
    }
  }

  getKeysMap(): Map<string, any> {
    return this.keysMap;
  }
}

interface MyType extends TKeyed {
  prop1: string;
  prop2: number;
}

// 传入包含所有键的样本对象,值可以是对应类型的默认值
const myInstance = new MyClass<MyType>({ prop1: '', prop2: 0 });
const keysMap = myInstance.getKeysMap();

console.log(keysMap); // 输出: Map(2) {'prop1' => null, 'prop2' => null}

这种方式简单直接,样本对象的键就是泛型T的键,运行时可以通过Object.keys正确获取。

方式二:利用类的原型(仅适用于类类型的T)

如果你的泛型参数T是类而不是接口,可以通过传入类的构造函数,从原型上获取键(但接口没有原型,所以这种方式不适用你的场景):

class MyTypeClass {
  prop1: string = '';
  prop2: number = 0;
}

class MyClass<T extends object> {
  private keysMap: Map<string, any>;

  constructor(cls: new () => T) {
    const prototype = cls.prototype;
    const keys = Object.getOwnPropertyNames(prototype).filter(key => key !== 'constructor');
    this.keysMap = new Map<string, any>();

    for (const key of keys) {
      this.keysMap.set(key, null);
    }
  }

  getKeysMap(): Map<string, any> {
    return this.keysMap;
  }
}

const myInstance = new MyClass(MyTypeClass);
console.log(myInstance.getKeysMap()); // 输出: Map(2) {'prop1' => null, 'prop2' => null}

但因为你的场景中T是接口,所以第一种方式更适合。


内容的提问来源于stack exchange,提问作者KM- MK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:50:21