TypeScript 的联合类型和交集类型的命名

我不能理解 TypeScript 中术语 联合类型交叉路口类型背后的逻辑。

实际上,如果不同类型的属性是一组属性,那么如果我将它们与 &操作符组合在一起,得到的类型将是这些属性集的 工会。按照这个逻辑,我希望这样的类型被称为 联合类型。如果我将它们与 |组合,我只能使用它们的公共属性,即集合的 十字路口

维基百科似乎支持这种逻辑:

任意给定的非空集 S 的幂集(所有子集的集合)构成一个布尔代数,一个集合代数,具有两个运算∧ : = ∪(联合)和∧ : = ∩(交)。

然而,根据 Typescriptlang.org,情况正好相反: &用于生产 交叉路口类型,而 |用于生产 联合类型

我相信还有其他的方法来看待这个问题,但是我想不出来。

6495 次浏览

The type A | B refers to objects which are either A or B. In other words, values of such type are drawn from the union of values for A and values for B.

The type A & B refers to objects which are both A and B. In other words, values of such type are drawn from the intersection of values for A and values for B.

The naming and semantics are identical in other languages such as C++.

Here's another way to think about it. Consider four sets: Blue things, red things, big things, and small things.

If you intersect the set of all blue things and all small things, you end up with the union of the properties -- everything in the set has both the blue property and the small property.

But if you took the union of blue small things and red small things, only the smallness property is universal in the resulting set. Intersecting "blue small" with "red small" produces "small".

In other words, taking the union of the domain of values produces an intersected set of properties, and vice versa.

In image form: enter image description here

The confusion here probably stems from how we imagine the sets, namely, thinking of the intersection/union as involving the members of types as opposed to the types themselves. I put together a graphic that hopefully clarifies the concept:

Union/Intersection diagram

You must not think of types as sets of object properties in this case. We can avoid the confusion about how union and intersection types work by looking at scalar variables and their sets of permissible values (instead of objects):

type A = 1 | 2
type B = 2 | 3
type I = A & B
type U = A | B


let a: A
let b: B
let i: I
let u: U


a = 1
a = 2
a = 3 // <- error


b = 1 // <- error
b = 2
b = 3


i = 1 // <- error
i = 2
i = 3 // <- error


u = 1
u = 2
u = 3

Here the terms "union" and "intersection" correspond exactly to the set theory terms when applied to the sets of permissible values.

Applying the notion of permissible values (instances) to object types is a bit trickier (because the set theory analogy doesn't hold well):

type A = {
x: number
y: number
}


type B = {
y: number
z: number
}


type I = A & B
type U = A | B
  • A variable of type A can hold object instances with properties x and y (and no other properties).
  • A variable of type B can hold object instances with properties y and z (and no other properties).
  • In set theory the intersection of the two sets of object intances above is empty. However, a variable of intersection type I can hold objects with the properties of type A AND those of type B (i.e. x, y, and z; hence the & symbol) which corresponds to the union of properties of the two types (hence the confusion).
  • In set theory the union of the two sets of object intances above does not include objects with all three properties. However, a variable of A2 U can hold objects with the properties of type A OR those of type B (logical OR, not XOR, i.e. x and y, y and z, or x, y, and z; hence the A0 symbol) which implies that the A3 of properties of the two types (y in our example) is guaranteed to be present (hence the confusion).
let i: I
let u: U


i = { x: 1, y: 2 };         // <- error
i = { y: 2, z: 3 };         // <- error
i = { x: 1, y: 2, z: 3 };


u = { x: 1, y: 2 };
u = { y: 2, z: 3 };
u = { x: 1, y: 2, z: 3 };

I also had the same question and couldn't understand how could it be seen in the opposite way. After reading the answers, I think I could explain it in the linguistic aspect providing different meanings of the two words and giving a room for the opposite naming approaches.

Compare the following meanings of word intersect:

The orbit of this comet intersects the orbit of the Earth.

In this sentence intersect means to cross at a point or set of points. Think of two things having something common (e.g. a point), and otherwise different. That's what is called intersection in math and SQL.

We need to pinpoint the place where maximum achievable conservation intersects with the highest potential financial return.

Here intersect means to come together and have an effect on each other, like two things becoming components of one new cool thing. That's the meaning of the word in TypeScript.

In a similar way you can think of union as an act of joining different things together in a loose sense - that is the meaning of union in math and SQL; but it can mean not just joining but joining together with a common interest or purpose which corresponds to the meaning of union in TypeScript.

Inspired (don't ask me why) by different translations of intersect into Russian: пересекать (also to cross) and скрещивать (also to crossbreed).

type Head = {
skin: string,
bones: string,
nouse: number,
eyes: number,
ears: number
}


type Body = {
skin: string,
bones: string,
arms: number,
foots: number
}


type Frankenstein = Head | Body


let frank: Frankenstein


`1 rule (only Head)`


frank = {skin: 'green', bones: 'skull', nouse: 1, eyes: 2, ears: 2}


`2 rule (only Body)`


frank = {skin: 'gray', bones: 'skeleton', arms: 2, foots: 2}


`3 rule (Body and Head all together)`
 

frank = {
skin: 'green',
bones: 'skull',
nouse: 1,
eyes: 2,
ears: 2,
arms: 2,
foots: 2
}


`4 rule (Frank without arms or foots or ears or ...)`


frank = {
skin: 'green',
bones: 'skull',
nouse: 1,
eyes: 2,
ears: 2,
foots: 2
}


frank = {
skin: 'green',
bones: 'skull',
nouse: 1,
eyes: 2,
ears: 2,
arms: 2
}


frank = {
skin: 'green',
bones: 'skull',
nouse: 1,
eyes: 2,
arms: 2,
foots: 2
}


`-1 rule (he can't exist without general parts)`


frank = {
bones: 'skull',
nouse: 1,
eyes: 2,
ears: 2,
foots: 2} //error


frank = {
skin: 'green',
nouse: 1,
eyes: 2,
ears: 2,
arms: 2} //error


`-2 rule (and the MOST NOTABLY he can not exist without full kit of one of
his parts, why - ask his best friend - TypeScript)`


frank = {
skin: 'green',
bones: 'skull',
eyes: 2,
ears: 2,
foots: 2} //error


frank = {
skin: 'green',
bones: 'skull',
nouse: 1,
eyes: 2,
arms: 2
} //error

They just updated the documentation the other day, and now it provides a clarifying description with a simple example:

It might be confusing that a union of types appears to have the intersection of those types’ properties. This is not an accident - the name union comes from type theory. The union number | string is composed by taking the union of the values from each type. Notice that given two sets with corresponding facts about each set, only the intersection of those facts applies to the union of the sets themselves. For example, if we had a room of tall people wearing hats, and another room of Spanish speakers wearing hats, after combining those rooms, the only thing we know about every person is that they must be wearing a hat.

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types

Think of it this way...

Intersection Types (A & B)

In the universe of objects, only those who are compatible with both A AND B are part of the domain of the intersection type; and therefore contain all properties of A and B (the union of both sets of properties). In other words, if the objects are sitting in the intersection of A and B, they possess all properties (the union) of both A and B.

Union Types (A | B)

In the universe of objects, only those who are compatible with either A OR B are part of the domain of the union type; and therefore contain all properties of A or all properties of B, potentially having some or no properties in common between A and B. In other words, if the objects are sitting in the union of A and B, they can either possess all properties of A or all properties of B, and potentially common properties (the intersection) of A and B.