我在一些TypeScript PR中看到了“同态映射类型”一词。这是一个示例:https://github.com/microsoft/TypeScript/pull/21919
在--strictNullChecks模式下,当同态映射类型删除?时。基础类型的属性中的修饰符,它也会从该属性的类型中删除未定义
什么是同态映射类型?同态到底是什么?是否有一个很好的例子,说明非同态映射类型
我感到困惑的原因是homomorphism是两个保留特定操作的结构之间的映射。这里有什么问题?也就是说,在f
是映射的情况下,以下等式中的op
是什么:
f(x op y) = f(x) op f(y)
我尝试假设op
是&
,该操作与两种类型相交。
同构映射将是这样的:
F<T & U> = F<T> & F<U>
同态映射(来自the TS handbook)的示例是:
type Partial<T> = { [P in keyof T]?: T[P] }
因为Partial<T & U>
始终与Partial<T> & Partial<U>
相同。
问题是我无法提出使映射类型非同态的任何方法!
即使是这样的傻子,也似乎是同态的:
type Silly<T> = { [P in "foo"]: number}
让我感到困惑的是,Silly
似乎是同构的(Silly <T & U>
= Silly<T> & Silly<U>
)。
这个似乎与handbook所说的同态映射类型相反:
...同态的,这意味着映射仅适用于T的属性,而不适用于其他属性。编译器知道在添加任何新的属性修饰符之前,它可以复制所有现有的属性修饰符
Silly
保留&
,但根据手册中的定义不是同态映射类型。
答案 0 :(得分:11)
在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 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
,其中K
extends
keyof T
是通用类型参数,T
是通用类型参数:
// <K extends keyof T, T> ... {[P in K]: ...}
type Hom4<T, K extends keyof T> = { [P in K]: 1 };
这特别使我们能够从对象的键的 some 复制属性修饰符,并且implemented here主要是为了使the Pick<T, K>
utility type同构。让我们看看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
和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
的关系。在NonHom1
中,键恰好是相同的,但是没有keyof
,因此编译器不会将映射识别为同态。在NonHom2
中,您使用的是keyof
,但是编译器会急切地求值KeysOfFoo
,以便到NonHom2
时,它与{{1 }}。在NonHom1
中,您仅遍历NonHom3
的{{1}}键(全部),但是再次,编译器丢失了线程并且不再识别{{1} }作为同态映射的触发器。有workarounds, see microsoft/TypeScript#24679,但是这里的要点是,如果您偏离string
或Foo
-其中-in Extract<keyof Foo, string>
-和两者-in keyof
-并且- in K
-通用,您将获得非同态映射。
我完成了。在此之后的几天里,我不想写“同态”一词。无论如何,希望能有所帮助;祝你好运!