使用大小写转换和泛型时如何修复ts(2322)?

时间:2019-06-06 13:55:21

标签: typescript

我一直在尝试使用Type来使用开关大小写来映射值。例如,获取数字的字符串版本。

function getNumber(id) {
  switch (id) {
    case 1:
      return 'one';
    case 2:
      return 'two';
    case 3:
      return 'three';
  }
}

我想使用Typescript来确保传递了有效的密钥,因此我尝试了以下接口,并使用它来提取允许的密钥

interface Numbers {
  1: 'one';
  2: 'two';
  3: 'three';
}

function getNumber<K extends keyof Numbers>(id: K) {
}

到目前为止一切顺利!实际上,如果我尝试getNumber(4)会收到错误消息。

问题发生在返回类型上。我希望返回Numbers[K] | void

function getNumber<K extends keyof Numbers>(id: K): Numbers[K] | void {
  switch (id) {
    case 1:
      return 'one';
    case 2:
      return 'two';
    case 3:
      return 'three';
  }
}

但是,我收到了错误Type '"one"' is not assignable to type 'void | Numbers[K]'.ts(2322)

在TS 3.5.1和以前的版本中,我没有看到此问题。

有什么主意吗?

1 个答案:

答案 0 :(得分:1)

此问题的发生是由于pull request使其进入3.5版本。它被称为breaking change。简而言之,如果您拥有类型为x的变量{a: string, b: number},类型为k的值"a" | "b"和类型为v的变量string | number,TypeScript曾经让您编写x[k] = v,现在却没有。以前,如果k恰好是"a"v恰好是1,则编译器会很高兴地给您竖起大拇指,然后您会在运行时崩溃您评估x.a.charAt(0)。按照控制--strictFunctionTypes compiler optionimprovement to calling unions of function types的相同规则,唯一允许的 safe 是有关类型的 intersection 。因此,如果x[k] = w的类型为w而不是string & number,则只能写string | number。对于此类原语,没有stringnumber类型的值,因此在实践中意味着它不会让您为x[k]分配任何内容而没有抱怨。

您的代码与recently filed Github issue中的代码非常相似,后者被关闭为“按预期工作”。对于像您这样的情况,这很不幸,但是这里的解决方案与您知道安全的但编译器不知道时使用的解决方案相同:type assertion

  function getNumber<K extends keyof Numbers>(id: K): Numbers[K] {
    switch (id) {
      case 1:
        return "one" as Numbers[K]; // assertion
      case 2:
        return "two" as Numbers[K]; // assertion
      case 3:
        return "three" as Numbers[K]; // assertion
    }
    throw new Error("this can't happen"); // ensure the compiler that all paths return
  }

overloads是可用于功能的另一个故意不健全的工具。编译器使您可以为函数内部的类型检查提供“宽松”实现签名,并为类型检查功能调用站点提供“更严格”的调用签名(或一组签名)。这种差异为您提供了克服此特定错误所需的空间。请注意,从道德上讲,它等同于类型断言,但在每个return语句中不需要太多键入:

  // call signature: strict
  function getNumber<K extends keyof Numbers>(id: K): Numbers[K];
  // impl signature: loose
  function getNumber(id: keyof Numbers): Numbers[keyof Numbers] {
    switch (id) {
      case 1:
        return "one";
      case 2:
        return "two";
      case 3:
        return "three";
    }
  }

最后,我不知道这是否满足您的用例,您可以完全放弃switch并使用适当类型的对象并使用索引访问。这在TS3.5中仍然可以接受:

    function getNumber<K extends keyof Numbers>(id: K): Numbers[K] {
      const numbers: Numbers = { 1: "one", 2: "two", 3: "three" };
      return numbers[id];
    }

好的,希望对您有所帮助。祝你好运!

Link to code