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
值都被视为有效字符串。
这是故意的吗?如果是,那为什么呢? :)
答案 0 :(得分:6)
TypeScript称为never
的类型在类型理论中称为bottom type,有时用符号“ with”表示。这个想法是它是(唯一)类型,没有该类型的值。您应该never
发现自己拥有该类型的值,因为它没有值。如果您将类型视为可能的值的集合,那么它就是empty set(符号“∅”)。
这也许对您有意义。
TypeScript也具有subtyping的概念。就像集合一样,类型可以通过包含一些相同的值来重叠。如果类型A
的每个值也是类型B
的值,则A
是B
的子类型。您也可以说A
扩展 B
,或者象征性地A <: B
。在TypeScript中,{a: string}
是object
的子类型,因为类型{a: string}
的每个值(例如,值{a: "hello"}
)也是类型{{1}的值}。
TypeScript的可分配性规则基本上与substitutability有关。如果变量的类型为object
和B
,则可以为该变量分配类型为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 <: T
和T
,它们是完全互补的并且没有共同的值,X
和Y
都是正确的。
用集合论的术语来说,基本上是说空集合是每个集合的子集。也就是说,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)。
这有意义吗?