TypeScript构造函数,其中一种泛型类型是显式的,而另一种是推断的

时间:2020-10-22 07:17:27

标签: typescript typescript-generics

想象一下此类描述一个由键引用的对象存储,该键是其属性之一:

class Store<T, K extends keyof T> {
    readonly key: K;
    readonly items: Map<T[K], T> = new Map();

    constructor(key: K) {
        this.key = key;
    }

    put(item: T) {
        this.items.set(item[this.key], item);
    }

    get(key: T[K]): T | undefined {
      return this.items.get(key);
    }
}

为使示例更具体,假设我们要在Store:s中保留两种类型的数据:

interface Person {
    name: string;
    address: string;
}

interface Product {
    id: number;
    name: string;
    category: string;
}

const personStore = new Store<Person, 'name'>('name'); // Stores Persons indexable by their name
const productStore = new Store<Product, 'id'>('id'); // Stores Products indexable by their id

personStore.put({name: 'Homer Simpson', address: '742 Evergreen Terrace'})
const homer = personStore.get('Homer Simpson');

productStore.put({id: 42, name: 'Pizza', category: 'Food'});
const pizza = productStore.get(42);

这可行,但令我感到困扰的是,Store:必须在创建属性时两次声明用作键的属性-一次作为类型参数,一次作为文字值。 现在,可以从给定的参数值推断出类型参数,但是在这种情况下,T不是参数的一部分,因此必须将其声明为类型参数。 K,但是构造函数的参数类型,因此可以推断出来。但是似乎无法在说明K的同时推断T

如果我完全省略了类型参数,则会将T推导为never,这会产生无用的对象,并且在构造过程中也会出错:

const someStore = new Store('name'); // Gives "argument of type name is not assignable to never"

我想要做到的是:

const personStore = new Store<Person>('name'); // T is Person, name is keyof Person. 

我考虑过声明一个构造函数接口,但这无济于事。创建静态工厂方法不仅可以返回完全类型化的泛型对象,而且在从T推导K时还无法指定key

我显然也不想仅在构造器中提供一个虚拟项来推断T

因此:是否有可能从参数中推断出一个泛型类型,同时陈述另一种类型呢?还是有一些聪明的解决方法?

1 个答案:

答案 0 :(得分:0)

interface Person {
    name: string;
    address: string;
}

interface Product {
    id: number;
    name: string;
    category: string;
}

class Store<T> {
    readonly key: keyof T;
    readonly items: Map<T[keyof T], T> = new Map();

    constructor(key: keyof T) {
        this.key = key;
    }

    put(item: T) {
        this.items.set(item[this.key], item);
    }

    get(key: T[keyof T]): T | undefined {
      return this.items.get(key);
    }

}

const personStore = new Store<Person>('name'); // Stores Persons indexable by their name
const productStore = new Store<Product>('id'); // Stores Products indexable by their id

personStore.put({name: 'Homer Simpson', address: '742 Evergreen Terrace'})
const homer = personStore.get('Homer Simpson');

productStore.put({id: 42, name: 'Pizza', category: 'Food'});
const pizza = productStore.get(42);

console.log(pizza)