【问题标题】:Why is never assignable to every type?为什么永远不能分配给每种类型?
【发布时间】:2019-03-25 07:04:30
【问题描述】:

TypeScript 文档是这么说的

never 类型是每个类型的子类型,并且可以分配给每个类型

但没有提及原因。

直观地说,我希望这样的代码会失败:

const useString = (str: string) => console.log('This is definitely a string:', str)
const useNever = (not_a_string: never) => useString(not_a_string)

但没有错误,因为任何never 值都被视为有效字符串。

这是故意的吗?如果是,那为什么? :)

【问题讨论】:

    标签: typescript


    【解决方案1】:

    TypeScript 调用never 的类型在类型理论中称为bottom type,有时用符号“⊥”表示。这个想法是它是没有该类型值的(唯一)类型。您应该never 发现自己持有该类型的值,因为它没有值。如果您将类型视为可能值的集合,那么它就是empty set(符号“∅”)。

    这对你来说可能都是有意义的。

    TypeScript 也有subtyping 的概念。就像集合一样,类型可以通过包含一些相同的值来重叠。如果A 类型的每个值也是B 类型的值,那么AB子类型。你也可以说A 扩展 B,或者象征性地,A <: B。在 TypeScript 中,{a: string}object 的子类型,因为 {a: string} 类型的每个值(例如,{a: "hello"} 的值)也是 object 类型的值。

    TypeScript 的可赋值性规则基本与substitutability 有关。如果变量的类型为BA <: B,则可以为该变量分配A 类型的值,因为A 类型的每个值也是B 类型的值。您不一定要反过来,将B 类型的值分配给A 类型的变量。除非B <: A,否则有一些B 类型的值不是A 类型的值。

    从作为值集的类型的角度来看,A <: B 就像是说A 类型的值集是subset 类型的值集B 的一个subset,(符号A ⊆ B)。

    这可能(我希望)对你也有意义。

    我们还需要一件事:逻辑 principle of explosion。如果您从一个错误的陈述开始,那么您可以从中证明任何东西。所以,假设“月亮是奶酪做的”是假的,那么“如果月亮是奶酪做的,那么今天就是星期三”是真的。此外,“如果月亮是奶酪做的,那么今天就不是星期三”是真的。把假的东西当真会产生可怕的后果:一切都会爆炸。 ? 这可能令人惊讶,但这是条件语句与其contrapositive 等价的直接结果。你可能对“如果今天不是星期三,那么月亮不是由奶酪制成的”和“如果今天是星期三,那么月亮不是由奶酪制成的”这句话或它们组合成“月亮不是由奶酪制成”感到满意奶酪,不管今天是什么日子”。

    如果您不接受爆炸原理(许多数学家和逻辑学家也有同样的感受),那么接下来的内容可能对您不利。但至少要意识到爆炸的原理与形式逻辑和 TypeScript 中使用的类型理论是一致的。它具有有用的结果,弥补了它的怪异。

    现在让我们把所有这些放在一起。让我们随机选择一个类型T,并提出问题:never <: T?这相当于问题“never 类型的每个值也是T 类型的值吗?”或者,以下陈述是否适用于所有值 x:“如果 xnever 类型的值,那么它也是 T 类型的值”?根据never 的定义,我们知道“xnever 类型的值”必须始终为false。并且根据爆炸原理,“如果xnever 类型的值,那么xT 类型的值”语句必须始终为true。因此,never <: T 对于任何T 都是true。即使您有两种类型 XY,它们完全互补并且不包含共同的值,never <: Xnever <: Y 都是正确的。

    在集合论的术语中,它基本上是说空集是每个集合的子集。也就是说,∅ ⊆ T 表示任何 T。这是集合论中完全没有争议的陈述,但可能会给您同样的错误感。在任何情况下,你永远不会找到一个既不是集合T 的元素的空集合元素。

    因此never 类型的值始终可以分配给任何其他类型的任何变量。幸运的是,实际上,在运行时,您不会有任何 never 类型的值。但是 TypeScript 允许赋值,因为它是类型安全的并且有一些有用的结果。

    请注意,您不能说反话。 T <: never 不是真的,除非 Tnever 本身。 string 类型的值不能分配给never 类型的变量,因为没有string 值也是never 值。随心所欲的可分配性规则只有一个方向。

    好的,我希望这是有道理的。我想继续讨论类型理论中的top type 以及它最近作为unknown 包含在TypeScript 中,以及它如何补充never,但如果我这样做,这个答案将是一本教科书。所以我现在就停下来。

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

    【讨论】:

      【解决方案2】:

      您没有调用useNever 函数。如果您尝试使用参数调用它,它将失败,因为值不能是never。但与往常一样,您可以使用类型保护来欺骗编译器,例如这会工作

      const test = (val: string | number) => {
           if (typeof val === "string") {
      
           } else if (typeof val === "number") {
      
           } else {
               useNever(val); // works, since val is not number or string it is implicitly never
           }
       }
      

      【讨论】:

      • 是的,但这并不是对“为什么永远不能分配给每种类型?”这个问题的真正答案。 :)
      【解决方案3】:

      类型检查如何用于分配?

      R 值(赋值的右侧)。验证它是否可以采用 L-value(赋值左侧)不能采用的值。如果存在任何此类值,则拒绝分配。否则没关系。

      让我们看例子:

      let x: number;
      let y: never;
      
      x = y; // OKAY, can assign any given `never` value to a number
      
      y = x; // Not OKAY, x can be, among other values, 1, which is can not be assigned to never
      

      它看起来很荒谬,因为赋值需要将一些数据移动到给定变量的指定存储中,并且不存在任何值,因为它不是运行时类型。但实际上,从 never 到任何其他类型的赋值虽然有效,但实际上不会运行(除非您使用类型断言欺骗 TypeScript)。

      这有意义吗?

      【讨论】:

        猜你喜欢
        • 2019-06-24
        • 2022-11-14
        • 2021-02-07
        • 1970-01-01
        • 2021-03-23
        • 2022-06-19
        • 1970-01-01
        • 1970-01-01
        • 2011-09-05
        相关资源
        最近更新 更多