“同态映射类型"是什么意思?意思? [英] What does "homomorphic mapped type" mean?
问题描述
我在一些 TypeScript PR 中见过术语同态映射类型".下面是一个例子:https://github.com/microsoft/TypeScript/pull/21919
I've seen the term "homomorphic mapped type" in a few TypeScript PRs. Here's an example: https://github.com/microsoft/TypeScript/pull/21919
在 --strictNullChecks 模式下,当同态映射类型删除 ?来自底层类型属性的修饰符,它还从该属性的类型中删除 undefined
In --strictNullChecks mode, when a homomorphic mapped type removes a ? modifier from a property in the underlying type it also removes undefined from the type of that property
什么是同态映射类型?究竟什么是同态?有没有非-同态映射类型
What is a homomorphic mapped type? What exactly is the homomorphism? Is there a good example of a non-homomorphic mapped type
我感到困惑的原因是 同态 是两个结构之间的映射,它保留了一个特定的手术.这里有问题的操作是什么?也就是说,f
是映射,op
在下面的等式中是什么:
The reason for my confusion is that a homomorphism is map between two structures that preserves a particular operation. What is the operation in question here? That is, where f
is the mapping, what is op
in the following equation:
f(x op y) = f(x) op f(y)
我尝试假设 op
是 &
,即两种类型相交的操作.
I tried going on the assumption that op
is &
, the operation that intersects two types.
一个同态映射是这样的:
A homomorphic mapping would then be one such that:
F<T &U>=F T&F
同态映射示例(来自TS 手册) 是:
An example of a homomorphic mapping (from the TS handbook) is:
type Partial<T> = { [P in keyof T]?: T[P] }
因为 Partial
总是与 Partial
.
问题是我想不出任何方法来使映射类型非同态!
The problem is that I can't come up with any way to make a mapped type non-homomorphic!
即使像这样愚蠢的人看起来也是同态的:
Even silly ones like this seem homomorphic:
type Silly<T> = { [P in "foo"]: number}
令我困惑的是 Silly
似乎是同态(Silly
= Silly
).
What's confusing for me is that Silly
seems to be a homomorphism (Silly <T & U>
= Silly<T> & Silly<U>
).
这似乎与handbook 说一个同态映射类型:
This seems to contradict what the handbook says a homomorphic mapped type:
...同态,这意味着映射仅适用于 T 的属性而不适用于其他属性.编译器知道它可以在添加任何新的属性修饰符之前复制所有现有的属性修饰符
...homomorphic, which means that the mapping applies only to properties of T and no others. The compiler knows that it can copy all the existing property modifiers before adding any new ones
Silly
保留了 &
但不是手册中定义的同态映射类型.
Silly
preserves &
but is not a homomorphic mapped type by the definition in the handbook.
推荐答案
在 TypeScript 中,同态映射类型具体是一种类型,编译器可以识别出您正在映射现有对象类型的属性.在这种情况下,输出对象类型的属性将具有与输入类型相同的 readonly
和/或可选 (?
) 属性修饰符.我知道有几种方法可以使映射类型同态,还有一些其他方法可以使它......不是.
In TypeScript, a homomorphic mapped type is specifically a type in which the compiler recognizes that you are mapping the properties of an existing object type. In such cases, the output object type will have the same readonly
and/or optional (?
) property modifiers on its properties as the ones on the input type do. There are a few ways I know of to make a mapped type homomorphic, and some other ways to make it... not.
接下来,让我们使用这种类型作为映射对象:
In what follows, let's use this type as something to map over:
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
类型上使用它们时会得到什么:
In the above, you are explicitly iterating over keyof
something. Let's see what you get when we use them on our 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;
} */
您可以看到,在所有输出中,只读和可选标记都是从输入中复制过来的.这是产生同态映射类型的主要技术,也是迄今为止最常见的技术.
You can see that in all the outputs, the read-only and optionality markers were copied over from the inputs. This is the main technique for producing homomorphic mapped types and by far the most common.
二次同态映射类型技术,in K
where K
extends
keyof T
是泛型类型参数,T
是泛型类型参数:
Secondary homomorphic mapped type technique, in K
where K
extends
keyof T
is a generic type parameter and T
is a generic type parameter:
// <K extends keyof T, T> ... {[P in K]: ...}
type Hom4<T, K extends keyof T> = { [P in K]: 1 };
这特别使我们能够从对象的一些键中复制属性修饰符,并且是在此处实施,主要是为了使 Pick
实用程序类型 同态.让我们看看 Hom4
与 Foo
的行为:
This specifically gives us the ability to copy property modifiers from just some of the keys of an object, and was implemented here, primarily to make the Pick<T, K>
utility type homomorphic. Let's see how Hom4
behaves with 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;
}*/
<小时>
现在几乎任何其他映射类型的使用都会给出一个非-同态类型.如果您不认为自己映射了不同对象类型的键,那么这并不是真正的问题.例如:
Now just about any other use of mapped types gives a non-homomorphic type. This is not really an issue if you don't see yourself as mapping over the keys of a different object type. For example:
type NonHom0 = { [P in "a" | "b" | "c"]: 0 };
/* type NonHom0 = {
a: 0;
b: 0;
c: 0;
}*/
NonHom0
的属性既不是可选的,也不是只读的;为什么会这样?没有其他带有键 a
、b
和 c
的类型可以复制它们.如果你开始想象你是从其他对象类型复制一个属性,事情会变得有点棘手,但编译器并不这么认为:
The properties of NonHom0
are neither optional nor read-only; why would they be? There's no other type with keys a
, b
, and c
to copy them from. Things get a little trickier if you start imagining that you're copying a property from some other object type, but the compiler doesn't see it that way:
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
时code>,它与 NonHom1
中的映射相同.在 NonHom3
中,您只迭代 Foo
的 string
键(它们都是),但同样,编译器丢失了线程并且不再将 Extract 识别为同态映射的触发器.有 解决方法,请参阅 microsoft/TypeScript#24679,但重点是这里就是如果你偏离
in keyof
或 in K
-where-K extends keyof T
-and-both-K
-and-T
-are-generic,你会得到非同态映射.
In those cases the mapping is non-homomorphic; the output types have neither read-only nor optional properties. (the | undefined
is still present on properties that used to be optional, but the properties themselves are not optional). It's true that you're still iterating over the keys of Foo
, but the compiler no longer sees a relationship with Foo
. In NonHom1
, the keys just happen to be the same, but there's no keyof
, so the compiler doesn't recognize the mapping as homomorphic. In NonHom2
, you're using keyof
, but the compiler eagerly evaluates KeysOfFoo
so that by the time you get to NonHom2
, it's the same mapping as in NonHom1
. In NonHom3
, you're iterating over just the string
keys of Foo
(which is all of them), but again, the compiler loses the thread and no longer recognizes in Extract<keyof Foo, string>
as a trigger for homomorphic mapping. There are workarounds, see microsoft/TypeScript#24679, but the point here is that if you stray from in keyof
or in K
-where-K extends keyof T
-and-both-K
-and-T
-are-generic, you will get the non-homomorphic map.
哇,我完成了.在这之后的几天里,我不想写同态"这个词.无论如何,希望有所帮助;祝你好运!
Whew, I'm done. I don't want to write the word "homomorphic" for a few days after this. Anyway, hope that helps; good luck!
这篇关于“同态映射类型"是什么意思?意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!