为什么TypeScript认为每个数字都是其自己的类型?

9
表达式1==2会导致TypeScript报错:Operator '==' cannot be applied to types '1' and '2'。为什么TypeScript将这些值视为不同类型(typeof运算符可预测地表明它们都是number类型)?这是一种特别适用于数字的设计决策,还是整个类型系统的副产品?允许比较字面量可能会造成什么危害?

1
这只是因为数字具有不同的值,而不是类型问题。 - kevinSpaceyIsKeyserSöze
3
那么这个表达式肯定只会返回 false,而不是导致脚本无法编译,对吗? - clb
好的,它确实编译通过了,只是有一个警告...但是,是的,这是一个有趣的问题。这里有一个重现链接:https://www.typescriptlang.org/play/#src=1%20%3D%3D%202%3B - deceze
2
@ZevSpitz,“为什么”并不是问题的关键。我在询问设计决策。 - clb
2
@deceze,这里没有警告,那就是一个错误,但是除非使用 --noEmitOnError 标志,否则该过程仍将发出 JavaScript。 - Nitzan Tomer
显示剩余3条评论
3个回答

7
在这个上下文中,12被视为所谓的文字数字。这意味着值1具有类型1,因此只能是1,同样的情况也适用于2。鉴于此,表达式1==2是没有意义的,因为1永远不可能等于2,或者更准确地说它们的类型不匹配,你不能把苹果和橘子比较。
以下是默认情况下文字类型假定的基本原理和详细信息: 关于文字类型: 文字类型有用的众多例子之一:

一堆只有一个成员的单例类型并不是很有用。为什么数字字面量不像非类型化的Javascript一样是多态的“Number”类型呢?这是一步退步,令人失望。人们不应该引入严格的静态类型系统,而不提供使其再次变得更加灵活的概念。 - user6445533
目前并没有明确的判断哪种方法更有益。对于某些情况,直接类型能够更好地工作,而对于其他情况,它们可能会带来问题。更多详细信息请访问此链接:https://github.com/Microsoft/TypeScript/pull/10676 - Trident D'Gao
关于你提供的“为什么字面类型很有用”的链接:标记联合的标签本身不一定是类型。它们可以像常量一样。Haskell、ML、Swift和Rust都采用了这种方法,我相信他们有充分的理由这样做。 - user6445533
重新表述以避免得出这是字面类型的唯一目的的结论 - Trident D'Gao
@ftor 在处理总和类型时,TypeScript比Haskell和ML更为先进,因为它委托给您区分JustNothing的问题,如果您足够聪明,可以拥有一个看起来像Just a | Right a的类型,而无需运行时崩溃。 - Trident D'Gao

5
当TypeScript对表达式1进行类型推断时,它会给它赋予类型1,而不是number类型。您可以通过检查以下代码来了解这一点:
const a = 1;

如果您使用IDE查询a的推断类型,您会发现a的类型是1。例如在TypeScript playground中,您将获得一个工具提示,其中包含const a: 1

因此,在if(1==2)中,1的类型为12的类型为2。由于它们具有不同的推断类型,TypeScript不允许您进行比较。这是TypeScript带给您的类型安全的一部分。
您可以通过以下方式解决此问题:
if (1 as number == 2) {
}

您在评论中提到,您正在进行1 == 2比较,因为编译器抱怨代码不可达,所以无法执行if (false) { ... }。我可以通过以下方式解决这个问题:

if (false as boolean) {
    console.log("something");
}

1 == 2无法编译时,我们获得了什么额外的类型安全性?从你的例子false as boolean可以看出,这个概念是有代价的。 - user6445533

4

Typescript可以从任何常量值创建类型。当与联合类型结合使用时,可以非常强大地表达函数接受的参数类型,例如:

function doStuff(p : "yes"| 1| true| "no"| 0| false ){

}

doStuff("maybe"); //Error
doStuff(3); // Error
doStuff(1) ; //OK

你遇到了不幸的副作用,像你这样的错误不会被报告为“表达式始终为假”,而是变成类型兼容性错误。

1
它的限制是有针对性的,Typescript 的主要目的是在你编写代码时强制执行限制,确保你所写的代码是有效的。例如 1 == 2 可能是一个错误,向函数传递不期望的参数(如上面的例子)也是一个错误。Typescript 希望在编译时尽可能多地捕获这些错误,而不是在运行时才发现。 - Titian Cernicova-Dragomir
我知道静态类型系统的目的。但显然TypeScript不知道多态性。 - user6445533
1
类型1和类型2都可以赋值给数字(基本类型)。这就是多态性。例如,在C#中,即使它们都继承自“形状”,也不能将“三角形”赋值给“正方形”。这是相同的原理。我很好奇您在这个实现中看到了什么问题。 - Titian Cernicova-Dragomir
字面类型单例增加了复杂性,却没有提供合理的好处。如果我有 a = true; b = true,那么如果类型与它们的值直接绑定,就不会给我带来任何东西。这只是一个值相等的问题,类型系统应该仅仅保护我免于比较不同类型。 - user6445533
好的,我明白你的意思。如果你对类型系统感兴趣,而且由于TypeScript是比较面向对象/子类型化的,你可能想要研究一下包括参数化/有界多态性、智能构造函数和和类型在内的函数式类型系统。 - user6445533
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接