“同态映射类型"是什么意思?意思? [英] What does "homomorphic mapped type" mean?

查看:47
本文介绍了“同态映射类型"是什么意思?意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一些 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 & 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 Kextendskeyof T 是泛型类型参数,T 是泛型类型参数:

Secondary homomorphic mapped type technique, in K where Kextendskeyof 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 实用程序类型 同态.让我们看看 Hom4Foo 的行为:

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 的属性既不是可选的,也不是只读的;为什么会这样?没有其他带有键 abc 的类型可以复制它们.如果你开始想象你是从其他对象类型复制一个属性,事情会变得有点棘手,但编译器并不这么认为:

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 中,您只迭代 Foostring 键(它们都是),但同样,编译器丢失了线程并且不再将 Extract 中的 识别为同态映射的触发器.有 解决方法,请参阅 microsoft/TypeScript#24679,但重点是这里就是如果你偏离 in keyofin 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆