typescript 在switch-cases中通过`never`进行耗尽检查是否有任何运行时用例?

5f0d552i  于 2023-06-07  发布在  TypeScript
关注(0)|答案(2)|浏览(266)

目前正在进行耗尽检查,例如TypeScript中的switch-case(参见:https://www.typescriptlang.org/docs/handbook/2/narrowing.html)。有一个switch on shape.kind属性的例子,看起来像这样:

type Shape = Circle | Square;
 
function getArea(shape: Shape) {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
    default:
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

现在,我的理解是,这个default检查只存在于编译时检查,* 即 * tsc将看到这个default-case,如果有可能发生never = something-else,例如。如果Shape是“三角形”,则定义如下:type Shape = Circle | Square | Triangle;,就像在error-example中一样,那么这将在编译期间抛出错误。
这是否意味着,这些穷举检查只在编译时才有用?是否存在这样的场景,其中这样的耗尽检查default块可以在运行时执行?

agyaoht7

agyaoht71#

是的,该块可以在运行时用任何东西调用:例如getArea("abc" as any)。在这种情况下,函数将返回字符串"abc"
完整Playground示例
因此,我总是倾向于在default块中抛出Exception:参见https://stackoverflow.com/a/52913382/1041641

vs3odd8k

vs3odd8k2#

这种详尽检查实际上只是为了在编译时提供帮助,以确保您已经显式地检查了每个单独的情况。只有当您不愿意让代码失败时,才需要这样的东西。如果你从每一个case开始执行return,并且在它之后不执行return,那么这是不必要的,因为编译器无论如何都会警告你,如果你犯了一个错误,并不是所有的代码路径都返回一个值:

function getArea(shape: Shape) { // okay
    switch (shape.kind) {
        case "circle":
            return Math.PI * shape.radius ** 2;
        case "square":
            return shape.sideLength ** 2;
    }
} 

function getAreaBad(shape: Shape) { // error! Not all code paths return a value.
    switch (shape.kind) {
        case "circle":
            return Math.PI * shape.radius ** 2;
        //case "square":
          //  return shape.sideLength ** 2;
    }
}

当然,从技术上讲,default块中的代码在运行时运行是可能的,但前提是TypeScript类型以某种方式被违反,例如非TS代码调用您的代码,或者TS代码使用不安全的类型Assert或不可靠的any类型。但这是一个独立的问题
这种对潜在的违反期望的处理对于某些用例可能很重要,但是这种详尽的检查不是任何人都应该编写的。
首先,在意外输入的情况下,它只返回输入,其中每个其他代码路径返回number。这意味着TypeScript将允许您将其视为number,并且您刚刚将爆炸移动到代码的后面:

try {
    const x = getAreaWithDefault("abc" as any);
    x.toFixed(); // looks fine to TypeScript
} catch (e) {
    console.log(e) // 💥 x.toFixed is not a function
}

也许如果它是return 0或其他一些数字,就会有一个合理的论点,即该块正在执行一些有用的运行时清理。
接下来,如果你真的关心随机输入,你必须预料到shape本身可能不允许你索引它来读取kind。因此,你可以传入nullundefined,代码将在你到达default块之前在你的函数中爆炸:

try {
    const y = getAreaWithDefault(null as any);
} catch (e) {
    console.log(e); // 💥 shape is null
}

因此,虽然某些用例可能需要比TypeScript认为需要的更多的运行时检查,但这与手册中的详尽检查几乎没有关系,其目的只是要求编译器在您未能显式处理情况时进行抱怨。
Playground链接到代码

相关问题