请考虑以下课程:
0: {Subject: "Maths", Grade: "11", Type: "Theory", MaxPrice: 1000}
1: {Subject: "Maths", Grade: "12", Type: "Theory", MaxPrice: 2000}
2: {Subject: "Maths", Grade: "13", Type: "Theory", MaxPrice: 3000}
3: {Subject: "Science", Grade: "10", Type: "Theory", MaxPrice: 4000}
length: 4
__proto__: Array(0)
此处的目标是允许对任何类型的对象建立索引,只要它们的export class GenericIndex<T> {
private indexableAttribute: keyof T
public constructor(indexableAttribute: keyof T) {
this.indexableAttribute = indexableAttribute
}
public addToIndex(someObject: T): void {
const indexValue: string = someObject[this.indexableAttribute]
}
}
类型为indexableAttribute
。是否有一种方法可以指定接口string
可以是具有T
值且属性为indexableAttribute
的任何对象?
当前,代码抛出一个
string
答案 0 :(得分:2)
此类唯一关心的属性是可索引的属性。因此T
可以扩展仅具有此一个属性的类型。它可能具有其他属性,并且通用参数会记住这些属性,但是您可以将此对象视为仅具有一个字符串键。而且由于打字稿需要知道该键的名称,所以它也必须是通用的。
因此,我们有一个字符串K
,可以是任何字符串,但是对象类型必须在属性K
处具有字符串才能通过类型检查。
所有这些都意味着这样的作品:
export class GenericIndex<T extends { [key in K]: string }, K extends string> {
private indexableAttribute: K
public constructor(indexableAttribute: K) {
this.indexableAttribute = indexableAttribute
}
public addToIndex(someObject: T): void {
const indexValue: string = someObject[this.indexableAttribute]
}
}
// Good:
new GenericIndex<{ a: string, b: number }, 'a'>('a')
// Type '{ a: string; b: number; }' does not satisfy the constraint '{ b: string; }'.
new GenericIndex<{ a: string, b: number }, 'b'>('c')
现在const indexValue: string = someObject[this.indexableAttribute]
之所以有效,是因为我们只知道T
有一个密钥K
,它是一个字符串。因此T[K]
必须是字符串。
但是正如您所看到的,这里有一个缺点。使用泛型,要么推断所有参数,要么全部显式。您不能显式地推断出另一个。
因此,无法推断可索引属性K
,因为无法推断T
。并且T
不能被推断,因为它不是构造函数的一部分。
因此您可以使用以下构造函数来解决此问题:
public constructor(indexableAttribute: K, objects?: T[]) {
this.indexableAttribute = indexableAttribute
if (objects) {
for (const object of objects) {
this.addToIndex(object)
}
}
}
现在可以让您执行以下操作:
// Inferred:
new GenericIndex('a', [{ a: 'abc', b: 123 }])
new GenericIndex('a', [] as { a: string, b: number }[] )
// Or omit the second argument and be the same as above.
new GenericIndex<{ a: string, b: number }, 'a'>('a')
答案 1 :(得分:1)
以下可能是您所需要的。
// The trick is to use this utility type
type SubType<Base, Condition> = Pick<Base, {
[Key in keyof Base]: Base[Key] extends Condition ? Key : never
}[keyof Base]>;
export class GenericIndex<T> {
private indexableAttribute: keyof SubType<T, string>;
public constructor(indexableAttribute: keyof SubType<T, string>) {
this.indexableAttribute = indexableAttribute;
}
public addToIndex(someObject: T): void {
// Unfortunately I still have to convert the value to `any` here,
// but our utility type can guard against misusage of our constructor
const indexValue: string = someObject[this.indexableAttribute] as any;
}
}
// Let's give it a try...
interface Test {
a: string;
b: number;
}
new GenericIndex<Test>("a"); // OK
new GenericIndex<Test>("b"); // Not OK