想象一下此类描述一个由键引用的对象存储,该键是其属性之一:
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
。
因此:是否有可能从参数中推断出一个泛型类型,同时陈述另一种类型呢?还是有一些聪明的解决方法?
答案 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)