【问题标题】:TypeScript Structural Type Checks - Union and Intersect TypesTypeScript 结构类型检查 - 联合和相交类型
【发布时间】:2020-04-14 01:04:38
【问题描述】:

我是打字稿的新手。我一直在玩联合和交叉类型的形状,并遇到了一些我没想到的东西......

如果我创建两个形状如下:

type Person = {
    name: string,
    occupation?: string
}

type Animal = {
    name: string
    gestationPeriodDays: number
}

然后我创建这两个形状的并集和一个交叉点,就像这样......

type AnimalUnionPerson = Animal | Person

let humanzeeAllFields: AnimalUnionPerson = {
    name: "Humanzee",
    gestationPeriodDays: 60,
    occupation: "Banana Farmer"
}

type AnimalIntersectPerson = Animal & Person 

let animalIntersectPerson = {
    name: "Shape Intersect",
    gestationPeriodDays: 24,
    occupation: "Data Scientist"
}

然后我创建了一个简单的函数,将交集类型作为参数...

function printOutIntersection(toPrint: AnimalIntersectPerson) {
    console.log(toPrint.name)
    console.log(toPrint.occupation) // With union of shapes, no need for User Defined Type Guard
    console.log(toPrint.gestationPeriodDays)
}

现在我可以将animalIntersectPerson 传递给函数,但不能将humanzeeAllFields 作为参数传递给函数,即使形状本身具有相同的字段。我希望能够给出由 TypeScript 执行的结构类型检查。谁能解释一下为什么会这样?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    事实上,AnimalUnionPersonAnimalIntersectPerson 并没有提供相同的结构:

    AnimalUnionPerson
    - name //required
    - occupation //optional
    - gestationPeriodDays //Because of the union typing this is optional
    
    AnimalIntersectPerson
    - name //required
    - occupation //optional
    - gestationPeriodDays //in this case it's required
    

    如您所见,gestationPeriodDays 状态的变化取决于使用联合类型或交集。

    【讨论】:

    • 我认为 TypeScript 进行类型检查的方式是结构性的,因此它查看变量具有的字段而不是您所说的变量的类型,因此声明类型的类型无关紧要,这是变量的形状。例如,我可以声明一个 Animal 类型的变量并将其传递给一个接受 Person 的函数,因为它具有 Person 所期望的必需的 name 字段。在这种情况下,Animal 类型没有链接到 Person 类型,TypeScript 只是检查变量的形状是否可以分配给函数期望的类型。
    • @Phil 这里的重点是形状不一样 - AnimalUnionPerson 类型不需要具有 gestationPeriodDays 属性,但 AnimalIntersectPerson 类型需要具有那个属性。
    • 享受这里的讨论让我对 TypeScript 有了更多的思考和学习。我在这里绝对得到了形状类型声明的不同之处,但我认为打字稿忽略了形状类型,一个变量在检查可分配性时声明为,并查看变量的结构是否可分配给预期的类型,并且在这个显然,这两个变量看起来完全一样,尽管它们被声明为不同的类型。
    • 我猜我的理解需要从看实际变量的结构是否可分配给类型和看变量声明为的类型的结构是否总是可分配的,无论变量的值如何。这是有道理的。
    【解决方案2】:

    路口类型

    当你有一个Animal & Person类型的值时,这意味着这个值保证是Animal AND Person

    联合类型

    当给定类型为Animal | Person 的值时,它可以是AnimalPerson。甚至可以两者兼而有之。与交叉点相比,不能保证值同时为AnimalPerson

    要表达一个值是Animal XOR Person,您可以使用discriminated union type(求和类型)。


    延续到您的示例:

    printOutIntersection 需要一个交集类型Animal & PersonhumanzeeAllFields 是联合类型Animal | Person,因此不能保证既是Animal 又是Person

    因为Person 不是assignableAnimalAnimal 单独有一个gestationPeriodDays 属性),所以使用humanzeeAllFields 类型的humanzeeAllFields 调用printOutIntersection 是无效的@。

    Playground

    【讨论】:

    • 这并没有解决结构子类型化的事实。例如,type A = { x: string, y?: string }type B = { x: string, z?: string },则A | B 可分配给A & B。因此,联合并不总是可以分配给交集。 Playground Link
    • 另外,我认为 TypeScript 进行类型检查的方式是结构性的,所以它查看变量的字段而不是你所说的变量的类型,所以类型是什么并不重要声明为,它是变量的形状。例如,我可以声明一个 Animal 类型的变量并将其传递给一个接受 Person 的函数,因为它具有 Person 所期望的必需的 name 字段。在这种情况下,Animal 类型没有链接到 Person 类型,TypeScript 只是检查变量的形状是否可以分配给函数期望的类型。
    • 我猜我的理解需要从看实际变量的结构是否可分配给类型和看变量声明为的类型的结构是否总是可分配的,无论变量的值如何。这是有道理的。
    • @kaya3 很好的提示。我无意表达联合永远不能分配给交集,更新答案。
    猜你喜欢
    • 2020-08-04
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    • 2019-12-14
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多