【问题标题】:How to fix ts(2322) when using a case switch and generics?使用大小写开关和泛型时如何修复 ts(2322)?
【发布时间】:2019-10-22 01:31:45
【问题描述】:

我一直在尝试使用 Typescript 使用 switch case 来映射值。例如,获取数字的字符串版本。

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 和以前的版本中看到了这个问题。

有什么想法吗?

【问题讨论】:

  • 您要返回Numbers[K] | void 还是Number | void
  • this change 的另一个牺牲品,has broken some things 以健全的名义。实际上,您必须使用断言之类的东西。
  • 如果可以的话,我会切换到this form...但不确定它是否适合您的用例
  • 您的代码可以工作in the playground,我在本地项目中对其进行了测试,没有出现错误……
  • 这是 Github 中的 very similar issue... 标记为“按预期工作”,所以我猜这里的答案是断言。

标签: typescript


【解决方案1】:

发生此问题是因为 pull request 已将其纳入 3.5 版本。它被称为breaking change。简而言之,如果你有一个类型为{a: string, b: number} 的变量x,一个类型为"a" | "b" 的值k,以及一个类型为vstring | number,TypeScript 用来让你编写x[k] = v,现在没有了。以前,如果k 恰好是"a"v 恰好是1,编译器会高兴地给你竖起大拇指,然后当你评估x.a.charAt(0) 时,你会在运行时炸毁。根据管理--strictFunctionTypes compiler optionimprovement to calling unions of function types 的相同规则,唯一允许的安全 是相关类型的交集。所以你只能写x[k] = w如果wstring &amp; 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

【讨论】:

    猜你喜欢
    • 2022-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多