首页 > 解决方案 > 为什么 TypeScript 中的 & 运算符过滤联合类型但连接对象类型?

问题描述

我在学习 TypeScript 时遇到了下面的代码,我不明白为什么&运算符仅在与联合类型一起使用时才返回'apple',但似乎连接对象类型,这可以通过它"name" | "age" | "address" | "year"在从结果类型中获取键时返回联合来证明。

type fruits = 'apple' | 'pear' | 'tomato'
type vegetables = 'spinach' | 'cabbage' | 'tomato';

type gardenPlants = fruits & vegetables;

type Employee = { name: string; age: number }
type Company = { name: string, address: string, year: number}

type Info = keyof (Employee & Company);

标签: typescript

解决方案


如果您有两种类型AB,那么交集类型 A & B的值必须是类型的值,A 也是类型的值B。这是一个交集,因为如果您将所有类型的值A和所有类型的值想象为欧拉图(通常称为“维恩图”,尽管有点用词不当)中的B圆圈,那么类型的值将是那些在圆圈重叠的交叉点。A & B

这在 TypeScript 中产生了一些有趣的结果,因为根据您是在考虑原语还是对象,还是对象,在某些人看来,它可能会倒退。(这可能是由于运算符keyof 逆变,但我离题了。)

对于字符串文字类型的联合,这相当简单:

type Fruits = 'apple' | 'pear' | 'tomato'
type Vegetables = 'spinach' | 'cabbage' | 'tomato';

type GardenPlants = Fruits & Vegetables;
// type GardenPlants = "tomato"

在这里,您的Fruits类型和Vegetable类型包含有限数量的特定字符串文字元素,它们之间只有一个是共同的。交点是这样"tomato"

对于对象类型,这可能会更令人困惑。重要的是要注意 TypeScript 中的对象类型是开放可扩展的;like 类型的值{a: string} 必须包含string-valueda属性,但它可以包含任何其他未提及的属性。这通常很有用,因为否则继承层次结构interface A { x: string }; interface B extends A { y: number }将无法形成类型层次结构。对象类型不是封闭的或精确的(请参阅microsoft/TypeScript#12936以了解引入精确类型的功能请求,以及多余的属性检查有时会欺骗人们认为对象类型是精确的特征)。

那么,给定

type Employee = { name: string; age: number }
type Company = { name: string, address: string, year: number }

考虑他的交叉点:

type CorporationsArePeopleToo = Employee & Company;

type 的值CorporationsArePeopleToo必须同时是 anEmployee a Company。对象类型是开放的,所以语言中没有说 anEmployee不能有 anaddress和 a year,也没有说 aCompany不能有 an age。因此,如果某事物具有 的所有属性Employee和 的所有属性Company,则它既是Employee又是Company,因此也是CorporationsArePeopleToo。您可以使用映射类型让编译器为您拼写出来:

type Expanded = { [K in keyof CorporationsArePeopleToo]:
  CorporationsArePeopleToo[K] };
/* type Expanded = {
    name: string;
    age: number;
    address: string;
    year: number;
} */

因此 a具有和的CorporationsArePeopleToo所有键:CompanyEmployee

type Info = keyof CorporationsArePeopleToo;
// type Info = "name" | "age" | "address" | "year"

如果您经历过这种“嘿,那是倒退”的想法,那是因为您正在考虑属性而不是。和keyof (A & B)是一样的(keyof A) | (keyof B)。由于keyof是逆变的,它将交集变成并集,反之亦然。

Playground 代码链接


推荐阅读