我正在使用TypeScript 2.9.2。我有一堂课:
class Class1 {
public prop1: number;
public prop2: number;
public prop3: number;
}
我声明了一个ReadOnlyArray
,代表Class1
键的子集:
private readonly keysSubset: ReadOnlyArray<keyof Class1> = ["prop1", "prop2"];
现在,我想声明一个变量,该变量可以采用keysSubset
中声明的任何属性名称:
const someKey: ? = this.keysSubset[1];
我既不想提供string
类型,也不想手动枚举如下所示的可能值:
const someKey: "prop1" | "prop2" = this.keysSubset[1];
我想明确地说“它可以包含来自keysSubset
的任何值”,所以当keysSubset
中的项目在代码中被静态更改(而不是在运行时)时,我不会必须手动更改someKey
类型声明,编译器仍然可以检查是否为someKey
分配了keysSubset
中的值之一。
更新(要提供更多基本原理信息):
我需要someKey
的确切类型来遍历keysSubset
并从类型Class2
的对象更新不同类Class1
的实例的某些属性:
class Class2 {
public prop1: number;
public prop2: number;
private readonly keysSubset: ReadOnlyArray<keyof Class1> = ["prop1", "prop2"];
public updateFrom(source: Class1): void {
this.keysSubset.forEach((someKey: ?/*keyof Class1 won't work since Class2 doesn't have prop3*/): void => {
this[someKey] = source[someKey];
});
}
}
简而言之,我要解决的问题是从不同类型Class1
的实例更新Class2
的给定属性子集。虽然子集是静态的,但每次属性更新都有一些通用逻辑,因此我不想只写一堆class1[someLey] = class2[someLey]
语句。
在这种情况下如何声明someKey
的类型?
答案 0 :(得分:1)
问题在于keysSubset
的类型是该类中所有键的数组。这是我们首先需要更改的内容,以便keysSubset
的类型正是分配值的数组。我们可以为此使用辅助函数,同时仍然将值约束为类的键。
要获取数组中项目的类型,我们可以使用类型查询。
class Class1 {
public prop1: number;
public prop2: number;
public prop3: number;
}
// Helper function to infer the subset of keys correctly
function keyArrayOf<T>() {
return function <K extends keyof T>(o: K[]) {
return o;
}
}
class Foo {
// will be inferred as ("prop1" | "prop2")[]
private readonly keysSubset = keyArrayOf<Class1>()(["prop1", "prop2"])
method(){
// Use a type query to get the type of an item of the keysSubset array
const someKey: Foo['keysSubset'][number] = this.keysSubset[1]; // "prop1" | "prop2"
// Or just let inference work
const someKeyInffered = this.keysSubset[1]; // "prop1" | "prop2", no need for an explicit annotation
}
}
修改
您可以使用keyArrayOf
帮助函数编写更新后的示例,
class Class2 {
public prop1: number;
public prop2: number;
private readonly keysSubset = keyArrayOf<Class1>()(["prop1", "prop2"])
public updateFrom(source: Class1): void {
this.keysSubset.forEach((someKey: Class2['keysSubset'][number]): void => {
this[someKey] = source[someKey];
});
}
}
答案 1 :(得分:0)
( @ TitianCernicova-Dragomir 的post包含了我最初问题的直接答案。此答案包含了如何声明编译器检查的答案-能够使用两个不相关类型的通用属性名称数组。感谢@Titian,我意识到我在回想:我不需要基于常量字符串数组值创建联合类型首先,我需要一个联合类型,该类型由Class1
和Class2
的通用属性名称组成,然后,我就可以声明一个数组因此,下面包含了该问题的答案。)
这里是如何声明仅包含其他两种类型的通用属性的类型:
type CommonProperty = keyof {
[P in keyof Class1 & keyof Class2]: Class1[P] | Class2[P]
}
现在,您可以声明一个包含所需公共属性子集的数组,该数组将由编译器检查值的正确性:
const keysSubset: ReadOnlyArray<CommonProperty> = [
"prop1" // Ok,
"prop2" // Ok,
"prop4" // Error - property is missing in Class1
];
方法的第一步是了解如何声明类型Class1
和Class2
中仅包含公共属性的类型:
type CommonFromClass1AndClass2 = {
[P in keyof Class1 & keyof Class2]: Class1[P] | Class2[P]
}
那对我来说是最复杂的部分。
现在,我们可以声明一个表示通用属性名称子集的字符串联合类型:
type CommonProperty = keyof CommonFromClass1AndClass2;
最后,我们可以在数组声明中使用它:
const keysSubset: ReadOnlyArray<CommonProperty> = [
"prop1" // Ok,
"prop2" // Ok,
"prop4" // Error - property is missing in Class1
];