如何在通用类中编写“ K扩展T的keyof”,然后使用K,然后

时间:2019-04-29 15:36:34

标签: typescript generics web

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

我创建一个接口,将其称为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}>();

我希望存储中的字段匹配通用定义(我认为T [K]),但是打字稿说密钥必须是字符串或数字类型,因此不能按预期工作。在我的界面中,我想说的是setFormField的第二个参数也是T [K]。 我还认为,我的类型定义是重复的。 Typescript也希望我提供一个秒参数,这对我来说很有意义,但是我不想每次都在函数上写K扩展T的key键,也不能再将其用作属性类型。

试图安静很多,但无法正常工作。.我实际上有点迷失了,因为我现在已经尝试了几个小时。以前从未与泛型有关,因此请对我保持谨慎。 :D

希望有人能帮助我。

2 个答案:

答案 0 :(得分: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()方法本身就是可以对任何属性起作用的通用函数K中的keyof T键。通用类型参数K不会出现在界面本​​身上。

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

您可以从上面的代码(或在Playground链接中)看到IntelliSense就是您想要的方式...已知调用T会返回类型getField("name")的值。 / p>

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

答案 1 :(得分:0)

您的密钥类型[key: K]无法由Typescript处理。两个对象作为键是什么样的?他们什么时候被认为是平等的?因此,记录/字典键的类型必须是字符串,数字或符号,而不能是其他任何内容。

您可以 通过使用诸如Array<[KeyType, ValueType]>之类的元组数组并按元组的第一个元素进行过滤来模仿您的尝试。