在 TypeScript 中,同态映射类型是编译器识别出您正在映射现有对象类型的属性的具体类型。在这种情况下,输出对象类型将具有相同的readonly
和/或可选(?
) 其属性上的属性修饰符与输入类型上的属性修饰符一样。我知道有几种方法可以使映射类型同态,还有一些其他方法可以使其...不。
接下来,我们使用此类型作为映射对象:
type Foo = {
norm: string,
opt?: string,
readonly ro: string,
readonly both?: string
};
主要同态映射类型技术,in keyof
:
type Hom1<T> = { [P in keyof T]: number };
type Hom2<T, U> = { [K in keyof (T & U)]: K extends keyof T ? "L" : "R" };
type Hom3 = { [Q in keyof { readonly a: string, b?: number }]: Q };
在上面,您明确地迭代了keyof
某物。让我们看看当我们在我们的应用程序上使用它们时您会得到什么Foo
type:
type Hom1Foo = Hom1<Foo>;
/* type Hom1Foo = {
norm: number;
opt?: number | undefined;
readonly ro: number;
readonly both?: number | undefined;
}*/
type Hom2FooDate = Hom2<Foo, { z: boolean }>
/*type Hom2FooDate = {
norm: "L";
opt?: "L" | undefined;
readonly ro: "L";
readonly both?: "L" | undefined;
z: "R";
} */
type Hom3Itself = Hom3
/* type Hom3Itself = {
readonly a: "a";
b?: "b" | undefined;
} */
您可以看到,在所有输出中,只读和可选标记都是从输入复制过来的。这是生成同态映射类型的主要技术,也是迄今为止最常见的技术。
二次同态映射类型技术,in K
where K
extends http://www.typescriptlang.org/docs/handbook/generics.html#generic-constraints keyof T
是一个泛型类型参数并且T
是泛型类型参数:
// <K extends keyof T, T> ... {[P in K]: ...}
type Hom4<T, K extends keyof T> = { [P in K]: 1 };
这特别使我们能够从刚刚复制属性修饰符some对象的键,并在中实现微软/TypeScript#29787 https://github.com/microsoft/TypeScript/pull/29787,主要是为了使the Pick<T, K>实用型 https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys同态。让我们看看如何Hom4
行为与Foo
:
type Hom4AllKeys = Hom4<Foo, keyof Foo>;
/* type Hom4AllKeys = {
norm: 1;
opt?: 1 | undefined;
readonly ro: 1;
readonly both?: 1 | undefined;
}*/
type Hom4SomeKeys = Hom4<Foo, "opt" | "ro">;
/* type Hom4SomeKeys = {
opt?: 1 | undefined;
readonly ro: 1;
}*/
现在几乎任何其他映射类型的使用都会给出non-同态类型。如果您不认为自己映射不同对象类型的键,那么这并不是真正的问题。例如:
type NonHom0 = { [P in "a" | "b" | "c"]: 0 };
/* type NonHom0 = {
a: 0;
b: 0;
c: 0;
}*/
的属性NonHom0
既不是可选的也不是只读的;为什么会这样?没有其他类型带钥匙a
, b
, and c
从中复制它们。如果您开始想象您正在从其他对象类型复制属性,但编译器不这么认为,事情就会变得有点棘手:
type NonHom1 = { [P in "norm" | "opt" | "ro" | "both"]: Foo[P] };
/* type NonHom = {
norm: string;
opt: string | undefined;
ro: string;
both: string | undefined;
}*/
type KeysOfFoo = keyof Foo
type NonHom2 = { [K in KeysOfFoo]: 1 }
/* type NonHom2 = {
norm: 1;
opt: 1;
ro: 1;
both: 1;
} */
type NonHom3 = { [Q in Extract<keyof Foo, string>]: Foo[Q] };
/* type NonHom3 = {
norm: string;
opt: string | undefined;
ro: string;
both: string | undefined;
}*/
在这些情况下,映射是非同态的;输出类型既没有只读属性也没有可选属性。 (这| undefined
仍然存在于曾经是可选的属性上,但属性本身不是可选的)。确实,您仍在迭代以下键Foo
,但编译器不再认为与Foo
. In NonHom1
,密钥恰好相同,但没有keyof
,因此编译器不会将映射识别为同态。在NonHom2
,你正在使用keyof
,但是编译器急切地计算KeysOfFoo
这样当你到达时NonHom2
,它与中的映射相同NonHom1
. In NonHom3
,你只是迭代string
的钥匙Foo
(这是全部),但是编译器再次丢失线程并且不再识别in Extract<keyof Foo, string>
作为同态映射的触发器。有解决方法,请参阅 microsoft/TypeScript#24679 https://github.com/microsoft/TypeScript/issues/24679#issuecomment-394710628,但这里的重点是,如果你偏离in keyof
or in K
-where-K extends keyof T
-和-两者-K
-and-T
-are-generic,你将得到非同态映射。
哇,我完成了。此后几天我都不想写“同态”这个词了。
Playground 代码链接 https://www.typescriptlang.org/play/#code/C4TwDgpgBAYg9nKBeKBvAsAKCjqA7OAJwFsAuKAZ2EIEs8BzAGi1yjjGAH5yraHnsuQhACGAEzh4ANiCiE4PanSYshoidNkAjOMAAW3SkoZYAvgG4sWAPTW0qANoBpKHSgBrCCDgAzKADpAgF1yQMDTLFBIKAAJOGIARgAeABUAPmQ0KAcABVc8Dy9fKBSQ-ABXYi0IQigLSPBoOOIAJlTGKABVDJRUbJc3T28-AAoUqAAyLoBKMpcIAA9gCDwxCkLhkqhOKAAiABldqHJdgCUj+swopviAZky+hwBFfI3ivuFxSRkoEUU+JhQLSGPCVaq1UxlF6XBrRZoJeCIFDwpKItKWTDWABUUGusXiCIQD1UOAIJHIoKqNQxrHYXApYJqUAAPlByqsID46BAxDS1F9NHIFBUqYQ+ThPhofjp9CDGbVWeyxJzubyzFjrFYro18a1EQARETLTLNNqIjp9ABe5B0cCkogKpjSNixeNNBqN0F6JPwRDIe0O4rYHEMByOio5XLwPKDku+snkJ0DPrjgplBiT4bZkdVQete3OGNMUA1WrddwAksAKBApH5kXcXbidc1blWa3XiYIJep478TiJdkHgSctFmlSro2rMMXS5gbHYkvMlis1m8-CkOukAoE0LlXk4ymEItq4fEACztKDL5ardZDYrb3rZPJuQ-kBJ1DGwm7Ec8AQSkKQnC8dYGz-VEEA6B8-DRDFsWbM8-0A4DQK7VgyX9BIgzpQxPwjZUoxjFNe0FRMoGwkiBWlXQMwolls0I3N1U1edT1-c8AGV4ggECQDA3VL3NPY6SzXZ5F2dEm3LP9uOIXi0O9btg3peiCMnYjlNTH5yMomc5y1WxfTwPR4jMwgwD0GgAGMyx1AA5SRmgABgeF9Xl2QcGN2MdvOs3Yylcy4ELxRy8Bc9DcD+KBnOHchYp9az4qLAz2KgML4Tc-c3F2TCxNE7yJO89MAvIRFciCL9pIcpz4ki0k-X+ZQcI4JqGAYiciOnVhyN4ZqfXTNr6A6nMpxS1if2vUCAHkfERTIYNgBBJoy+IWiygYCj4ihZsRMpPxPEKavCtb6t9ckKJa4AP1jYU9NYQbLrMEsJrS1biHuZ9nleABRJZCBEazgCSRbhL6hg0jKcqnkq4KcVC2qPrOzChquoaRqYsaU2FcH6GHWj0fUrrxqAA