为什么从不分配给每种类型?

时间:2018-11-29 13:35:13

标签: typescript

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值都被视为有效字符串。

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

3 个答案:

答案 0 :(得分:6)

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

这也许对您有意义。

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

TypeScript的可分配性规则基本上与substitutability有关。如果变量的类型为objectB,则可以为该变量分配类型为A <: B的值,因为类型为A的每个值也是一个值A类型的。您不必执行相反的操作,即将类型B的值分配给类型B的变量。除非A,否则某些类型为B <: A的值不是类型为B的值。

从“值集类型”的角度来看,A就像说类型A <: B的值集是subset的值集类型A,(符号B)。

(我希望)这对您也很有意义。

我们还需要一件事:逻辑principle of explosion。如果您从一个错误的语句开始,那么您可以从中证明任何内容。因此,假设“月亮是由奶酪制成”是错误的,那么“如果月亮是由奶酪制成,那么今天是星期三”是正确的。同样,“如果月亮是用奶酪制成的,那么今天不是星期三”。采取虚假的说法会带来可怕的后果:一切都会爆炸。这可能令人惊讶,但这是条件语句与其contrapositive等效的直接结果。您可能对以下句子感到满意:“如果今天不是星期三,那么月亮不是由奶酪制成的”和“如果今天不是星期三,那么月亮不是由奶酪制成的”,或者将它们组合成“月亮不是由奶酪制成”无论今天是星期几”。

如果您不接受爆炸原理(许多数学家和逻辑学家也有同样的想法),那么接下来的事情可能对您来说就不那么令人愉快了。但是至少要意识到爆炸的原理与TypeScript中使用的形式逻辑和类型理论是一致的。而且它具有有用的后果,弥补了它的怪异。

现在让我们将所有这些放在一起。让我们随机选择一个类型A ⊆ B,然后问一个问题:T吗?这等效于问题“每个never <: T类型的值 也是一个never类型的值吗?”或者,以下语句是否对所有值T都是正确的:“如果x是类型x的值,那么它也是类型never的值”?根据{{​​1}}的定义,我们知道“ T是类型never的值”必须始终为 false 。根据爆炸原理,语句“如果x是类型never的值,则x是类型never的值”必须始终为。因此,x对于任何T true 。即使您拥有两种类型的never <: TT,它们是完全互补的并且没有共同的值,XY都是正确的。

用集合论的术语来说,基本上是说空集合是每个集合的子集。也就是说,never <: X代表任何never <: Y。在集合论中,这是完全没有争议的陈述,但可能会给您同样的错误感。无论如何,您永远找不到空集合的元素,该元素也不是集合∅ ⊆ T的元素。

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

请注意,您不能说相反的话。除非never本身就是never,否则T <: never是不正确的。无法将类型T的值分配给类型never的变量,因为没有string的值也不是never的值。万事俱备的可分配性规则仅在一个方向上。

好的,我希望这是有道理的。我想继续探讨类型理论中的top type以及它最近在TypeScript中作为unknown所包含的内容,以及它如何对string起到补充作用,但如果我做所以我现在就停止。

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

答案 1 :(得分:2)

您没有调用useNever函数。如果尝试使用参数调用它,则它将失败,因为值不能为never。但是像往常一样,您可以使用typeguards欺骗编译器,例如这将起作用

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
     }
 }

答案 2 :(得分:0)

类型检查如何用于分配?

R值(赋值的右侧)。验证是否可以假定 L值(赋值左侧)不能接受的值。如果存在任何此类值,则拒绝分配。否则就可以了。

让我们看看示例:

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)。

这有意义吗?