【问题标题】:How to write "K extends keyof T" in a generic class and use K then如何在泛型类中编写“K extends keyof T”然后使用 K
【发布时间】:2026-02-15 05:25:05
【问题描述】:

我想创建一个类,它获取一个对象作为类型参数,然后将该类型用作该类中属性的类型,这样我只能设置和获取值,如果该键存在于该对象中。

我创建了一个接口,我们称之为 IStorage。 Storage 类应该实现 iStorage 并根据给定对象中是否存在键来存储值。

interface iStorage<T, K extends keyof T> {
    getField(k: K): T[K];

    setField(k: K, v: string | number | object): T[K];

    render(): void;
}

class Storage<T, K extends keyof T> implements iStorage<T, K> {
    private fields: { [key: K]: string | number | object };

    public getField(k: K): T[k] {
        return this.fields[k];
    }

    public setField(k: K, v: string | number | object): T[k] {
        return (this.fields[k] = v);
    }
}


let storage = new Storage<{name: string, type: number}>();

我希望 Storage 中的字段与通用定义匹配(我认为是 T[K]),但 typescript 说 key 必须是字符串或数字类型,所以这不能按预期工作。在我的界面中我想说的是,setFormField 的第二个参数也是 T[K]。 我也认为,我的类型定义是重复的。 Typescript 也希望我提供 seconds 参数,这对我来说很有意义,但我不想每次都在函数上编写 K extends keyof T,也不能将其用作属性类型。

尝试了很多安静,但无法让它工作..实际上我有点迷茫,因为我现在已经尝试了几个小时。以前从来没有用过仿制药,所以请对我温柔一点。 :D

希望有人能帮帮我。

【问题讨论】:

  • 那么,您是否最希望像Storage&lt;T&gt; 这样的东西充当T 类型的单个对象的包装器,您可以通过方法而不是点访问来获取和设置字段?我试图了解您希望Storage&lt;T, K&gt; 中的K 做什么。

标签: typescript generics web


【解决方案1】:

我不能 100% 确定您的用例,但我倾向于将您的代码更改为如下内容:

interface IStorage<T extends object> {
  getField<K extends keyof T>(k: K): T[K];
  setField<K extends keyof T>(k: K, v: T[K]): T[K];
  render(): void;
}

class Storage<T extends object> implements IStorage<T> {
  constructor(private fields: T) { }
  render() { }

  public getField<K extends keyof T>(k: K) {
    return this.fields[k];
  }

  public setField<K extends keyof T>(k: K, v: T[K]): T[K] {
    return (this.fields[k] = v);
  }
}

let storage = new Storage({ name: "str", type: 123 });
const str = storage.getField("name");
const num = storage.setField("type", 456);

Playground link

这里的IStorage接口只依赖于对象的类型T,而单独的getField()setField()方法本身就是通用函数,可以作用于keyof T中的任何属性键K .泛型类型参数K 不会出现在接口本身上。

我已更改 Storage 的实现以匹配。请注意,我已使构造函数采用 T 类型的值,该值存储为私有成员。如果您不想传入初始值,则必须将 T 限制为具有所有可选属性的类型(即,使用类似 Partial&lt;T&gt; 的东西,而不仅仅是 T。)

您可以从上面的代码(或在 Playground 链接中)看到 IntelliSense 是您想要的方式...调用 getField("name") 会返回一个类型为 string 的值。

好的,希望对您有所帮助。祝你好运!

【讨论】:

  • 谢谢,这就是我想要的(游乐场)! :) 在看了一段时间(和测试)之后,它安静合乎逻辑,但仍然让我很困惑。肯定需要更多地使用泛型来正确理解如何实现它们。谢谢。 :)
【解决方案2】:

Typescript 无法处理您的密钥类型 [key: K]。两个对象作为键会是什么样子?他们什么时候被认为是平等的?因此,Records/Dictionary 键的类型必须是字符串、数字或符号,仅此而已。

可能可以通过使用元组数组(例如 Array&lt;[KeyType, ValueType]&gt;)来模仿您正在尝试的内容,并按元组的第一个元素进行过滤。

【讨论】: