【问题标题】:TypeScript Array of TuplesTypeScript 元组数组
【发布时间】:2020-06-03 13:07:04
【问题描述】:

所以我有这个简单的例子:

type SomeTuple = [string, number];

const foo = (options: SomeTuple[]) => console.log(options);

const options = [
    ['first', 1],
    ['second', 2],
];

const otherOptions: SomeTuple[] = [
    ['first', 1],
    ['second', 2],
];

foo(options); // Error

foo(otherOptions); // OK

基本上没什么好说的了,我只是想不通为什么在我没有明确提供选项类型的情况下 TS 一直向我显示错误;

Playground

感谢您的宝贵时间!

【问题讨论】:

  • 您需要明确缩小类型,否则 Typescript 会假定您处理的是数组,而不是元组。

标签: arrays typescript types tuples


【解决方案1】:

TypeScript 将 options 的类型推断为 (string | number)[][]——一个包含字符串或数字的数组。它不会推断[string, number][]

这是需要指定类型的情况,如您的otherOptions

const otherOptions: SomeTuple[] = [
    ['first', 1],
    ['second', 2],
];

TypeScript 推理非常好,但它不能为您处理这种特定情况。假设您不会在其他代码中执行 options.push([1, 2]),这对 options 的限制太大。

在这种情况下,即使 as const 也无济于事,因为数组条目太具体(您不能将 readonly ['first', 1] 分配给 SomeTuple)。

【讨论】:

    【解决方案2】:

    默认情况下['first', 1] 是一个包含字符串或数字的数组,或者可以说它的类型是(string|number)[]。请注意,数组没有指定它的长度,也没有指定哪个元素是字符串或哪个是数字。你可以 push 一个包含 3 个或更多值的项目,TypeScript 就可以了。

    在您的第二个示例 (['first', 1]) 中,它是 [string, number][]。请注意,这与(string|number)[] 不同。现在它知道这是一个包含两个元素的数组,第一个是字符串,第二个是数字。为了能够帮助 TypeScript 解决这个问题,需要显式声明。

    如果从一开始就推断[string, number][],您将无法再添加[123, 'foobar'][123][]。这将导致更多令人困惑的错误,因为您必须改为为每种情况指定类型(修复一个问题,返回其他问题)。如果它基于整个文件或代码库进行推断,那么对于一个复杂的程序,编译将需要数年时间。

    【讨论】:

      【解决方案3】:

      看起来 Typescript 认为选项是 (string | number)[] 类型,所以如果你改变你的类型定义如下,你不应该得到选项或其他选项的编译错误。希望这会有所帮助。

      type SomeTuple = (string | number)[];
      

      【讨论】:

      • 我不知道为什么投反对票@downvoter。请解释一下。
      • 有人似乎对这个问题的所有答案都投了反对票。
      • 谢谢@T.J.克劳德。我赞成每个人的回答,希望能消除负面观点。
      • 也就是说,请注意您对SomeTuple 的重新定义与OP 对SomeTuple 的定义非常不同。据推测,如果他们对 stringnumber 以任何顺序或重复感到高兴,他们就不会像他们那样定义 SomeTuple
      • 希望只有那些值得支持的。对规范进行投票是没有用的。 :-)
      【解决方案4】:

      看起来Tuple 声明被错误地推断,因此您可以明确声明您的类型:

      foo = (options: SomeTuple[]) => console.log(options);
      
      options: SomeTuple[] = [
          ['first', 1],
          ['second', 2],
      ];
      
      otherOptions: SomeTuple[] = [
          ['first', 1],
          ['second', 2],
      ];
      

      A work stackblitz example can be seen here.

      【讨论】:

      • @T.J.Crowder 哎呀,对不起,我误解了这个问题。我已经编辑了回复。谢谢!
      • 我不是上面提到的@downvoter,但我对此表示反对,因为它既没有回答问题也没有添加任何新信息。 OP说“如果我做A,错误,如果我做B,没有错误。为什么?”所有这些答案都是“做 B 两次 - 没有错误。”
      【解决方案5】:
      type TypeOfOptions = typeof options[number] // (string | number)[]
      type IsTypeOfOptionsSubsetOfSomeTuple = 
        TypeOfOptions extends SomeTuple ? true : false // false is not assignable to SomeTuple
      
      

      这意味着TypeOfOptions 只是比SomeTuple 更通用的类型,自然不能用作SomeTuple,这正是您出现错误的原因。你想要[string, number]你通过自动推理得到(string | number)[],显然不一样。

      为什么它更通用,因为 TS 类型推断就是这样工作的,我们可以通过 as: 将我们的类型断言为更具体来更改行为,或者创建值构造函数来执行此操作。

      以下几个可能的选项如何更改初始推理:

      // value constructor
      const makeOptions = (...a: SomeTuple[]): SomeTuple[] => a
      const options1 = makeOptions(
          ['first', 1],
          ['second', 2],
      ) // a is SomeTuple[]
      
      // using :
      const options2: SomeTuple[] = [
          ['first', 1],
          ['second', 2],
      ];
      
      // using as
      const options3 = [
          ['first', 1],
          ['second', 2],
      ] as SomeTuple[]
      
      foo(options1) // ok
      foo(options2) // ok
      foo(options3) // ok
      

      关于评论中问题的附加说明。一般SomeTuple类型是TypeOfOptions类型的规范,这意味着如果你有TypeOfOptions的值,它也可以是SomeTuple,但不是必须的。让我们检查一下这种关系是否正确:

      type IsSomeTupleSubsetOfTypeOfOptions = SomeTuple extends TypeOfOptions ? true : false // true
      

      所以是的,SomeTupleTypeOfOptions 的特定变体。这意味着我们可以将TypeOfOptions 的类型断言为SomeTuple,表示我们正在缩小到这种特定情况。但如果我们这样做 - TS 将立即验证缩小类型的值,如果匹配,则类型断言成功。

      【讨论】:

      • 感谢您的回答!仍然困扰我的一件事是,在这种特殊情况下,type SomeTuple = [string, number] 的行为方式与type SomeArray = Array<string | number> 相同,对吧?。
      • @DeltaBravo - 不,[string, number]Array<string | number> 不同。 [string, number] 特别需要一个双元素数组,第一个元素是字符串,第二个元素是数字。
      • @T.J.Crowder,嗯,是的,谢谢!我误解了type TypeOfOptions = typeof options[number]
      • @DeltaBravo 在答案中添加了更多信息,希望对您有所帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-20
      • 2019-09-20
      • 1970-01-01
      • 2020-04-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多