【问题标题】:Why doesn't TypeScript show an error when assigning an object with exceeding properties to a variable?为什么 TypeScript 在将具有超出属性的对象分配给变量时不显示错误?
【发布时间】:2020-01-21 04:13:05
【问题描述】:

当给数据结构赋值时,TypeScript 引擎会检查它的类型是否正确。

let e = '5'

const a: number = '5'
const b: number = e

tsc 显示 2 个错误,说 ab 都被分配了一个与其类型不匹配的值。

但是,当使用接口作为类型时,如果分配的值取自另一个数据结构并包含超出属性,则 tsc 不会显示错误:

interface model {
  prop: number;
}

const tooManyProps = { prop: 1, prop2: 5 }
const notEnoughProps = {}

const a: model = tooManyProps
const b: model = notEnoughProps
const c: model = { prop: 1, prop2: 5 }

tsc 显示 bc 的错误,但不是 a,虽然我希望它会这样。

为什么我在分配 a 具有太多道具的对象时没有收到任何错误?

【问题讨论】:

标签: typescript


【解决方案1】:

Typescript 允许额外的属性。所以a: model = tooManyProps 不是类型错误,因为a 将作为model “工作”。 c: model = { prop: 1, prop2: 5 } 不起作用,因为 Typescript 不允许在对象文字中使用额外的属性。这绝对是 Typescript 的警告,但这是设计使然。

【讨论】:

    【解决方案2】:

    TypeScript 使用结构(或鸭子)类型范式进行类型检查。例如,如果 B 具有对象 A 的所有必需属性,我们可以将对象 A 分配给对象 B(即使 B 可能具有不属于 A 的其他属性)。

    当我们生成一个对象并将其分配给一个变量时,我们可以毫无问题地将它传递给任何组件(函数、类、模块),只要该对象具有该组件所期望的所有属性。因此,如果一个组件获得了一个具有它所期望的所有属性的对象,TypeScript 就会很高兴;当我们使用变量传递对象时,它不会检查多余的属性。

    然而,当我们创建一个对象时,就像这个例子:

    interface model {
      prop: number;
    }
    
    const c: model = { prop: 1, prop2: 5 }
    

    在这种情况下,TypeScript 会检查多余的属性,因为通常当我们显式创建给定类型的对象时(使用对象字面量),我们希望避免多余的属性。否则,我们可以明确地说“模型”类型可能具有使用索引签名的额外属性,如下所示:

    interface model {
      prop: number;
      [key: string]: any;
    }
    

    所以,希望这会有所帮助。

    相关的主题是“结构类型”和“多余的属性检查”。您可以在 Google 上搜索更多信息。

    【讨论】:

    【解决方案3】:

    TypeScript lint 在为接口分配太多 props 时不会显示任何错误,因为 TypeScript 中接口的定义说编译器只检查至少那些required 存在并匹配所需的类型。 因此,当您尝试传递与您的 prop 参数不匹配的内容时会看到错误,但当您尝试传递 prop 时不会道具2

    希望对你有帮助。

    您可以在此处找到更多详细信息: https://www.typescriptlang.org/docs/handbook/interfaces.html

    【讨论】:

      【解决方案4】:

      您的示例之间存在显着差异。您首先考虑的分配值的类型彼此之间没有关系。我所说的关系是交集,所以值是两种类型的成员。没有同时为stringnumber 的值。它只能是stringnumber,这就是为什么你不能将string 类型的成员分配给预期number 的地方。

      对于产品类型(产品是 js 对象,它是 ts 记录)我们有不同的故事,因为一种类型可以包含所有字段,这些字段也属于另一种类型。

      type SmallerT = {
        a: string
      }
      type BiggerT = {
        a: string,
        b: string
      }
      

      BiggerTSmallerT 的超集,在任何需要SmallerT 的地方都可以使用BiggerT 代替。在更多的接口概念中,我们可以说BiggetT 实现SmallerT 或扩展SmallerT

      这种行为对于 TS 至关重要,因为所有类型/接口只是产品和联合类型的别名,这意味着具有相同结构的一种类型实际上与另一种类型相同。它的structural typying

      type A = { a: string }
      type B = { a: string }
      

      类型AB 相等,相同,我们可以使用A,其中B 是必需的,或者其他方式。

      让我们回到前面的例子,换个方式写:

      interface SmallerT {
        a: string
      }
      interface BiggerT extends SmallerT {
        b: string
      }
      

      上面的类型声明与前一个相同,我们只是使用extend 来连接这两种类型,但结构上没有任何变化,仍然可以在需要SmallerT 的地方分配BiggerT

      考虑下面的树类型定义相等:

      type SmallerT = {
        a: string
      }
      type BiggerT = {
        a: string,
        b: string
      }
      
      interface SmallerT {
        a: string
      }
      interface BiggerT extends SmallerT {
        b: string
      }
      
      type SmallerT = {
        a: string
      }
      type BiggerT = SmallerT & {
        b: string
      }
      

      类型BiggerT 包含SmallerT 并且可以代替它使用,它满足SmallerT 的所有要求。

      我们也可以问TS,我们的BiggerT是否可以分配给SmallerT

      type IsBiggerTAssignableToSmallerT = BiggerT extends SmallerT ? true : false
      // evaluates into true for any of definitions presented above
      
      type IsNumberAssignableToString = number extends string ? true : false
      // evaluates to false as expected
      

      【讨论】:

        猜你喜欢
        • 2021-07-31
        • 2023-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-26
        • 1970-01-01
        • 2019-08-25
        相关资源
        最近更新 更多