题
TypeScript 规范在 §4.15.6 中规定了有关 &&
操作员:
&& 运算符允许操作数为任何类型并生成 结果与第二个操作数类型相同.
在 JavaScript 中, &&
如果操作数为假,则返回第一个操作数,否则返回第二个操作数 (参见 ECMA-262 §11.11).
这意味着如果左操作数为假, &&
将返回与左操作数类型匹配的值。例如,
typeof ( false && {} ) === "boolean" // true
typeof ( '' && 1 ) === "string" // true
typeof ( null && "hello" ) === "object" // true
typeof ( NaN && true ) === "number" // true
根据上面引用的规则,Typescript 将 不正确地 预测上述表达式的类型为 Object
, Number
, String
和 Boolean
, , 分别。
我错过了什么吗?是否有充分的理由来创建一个类型 &&
表达式与第二个操作数的类型匹配吗?结果类型不应该像 ||
运算符,并返回两个操作数的最佳公共类型,以及 Any
如果没有最佳通用类型?
解决方案
长话短说,这里没有让所有人满意的解决方案。
考虑一下这个常见的习语:
var customer = GetCustomer(...); // of type 'Customer'
var address = customer && customer.address;
if(address) {
printAddressLabel(address); // Signature: (Address) => void
} else {
// Couldn't find the customer or the customer has no address on file
}
放弃并决定“地址”为“任意”是非常蹩脚的,因为“客户”和“地址”之间没有最佳通用类型。
在大多数使用 && 运算符的情况下,类型 已经 match 或 && 正在以如上所述的值合并方式使用。无论哪种情况,返回正确操作数的类型都会为用户提供预期的类型。
虽然此时类型安全在技术上已经崩溃,但它并没有以可能导致错误的方式进行。您要么要测试结果值的真实性(在这种情况下,类型或多或少不相关),要么要对某些操作使用假定的正确操作数(上面的示例同时执行这两种操作)。
如果我们查看您列出的示例并假装左操作数不确定是真还是假,然后尝试编写对返回值进行操作的理智代码,它就会变得更加清晰 - 您无能为力 做 带有“false && {}”,尚未进入“任何”参数位置或真实性测试。
附录
由于有些人不相信上述内容,这里有一个不同的解释。
让我们假设 TypeScript 类型系统添加了三种新类型: Truthy<T>
, Falsy<T>
, , 和 Maybe<T>
, ,表示类型的可能的真值/假值 T
. 。这些类型的规则如下:
Truthy<T>
行为完全像T
- 您无法访问 的任何属性
Falsy<T>
- 类型的表达式
Maybe<T>
, ,当用作条件时if
块,变成Truthy<T>
在同一个身体里if
块和一个Falsy<T>
在里面else
堵塞
这会让你做这样的事情:
function fn(x: Maybe<Customer>) {
if(x) {
console.log(x.address); // OK
} else {
console.log(x.phone); // Error: x is definitely falsy
}
console.log(x.name); // Warning: x might be falsy!
}
到目前为止还不错。现在我们可以弄清楚 && 运算符的类型规则是什么。
Truthy<T> && x
应该是一个错误 - 如果知道左边是真的,你应该刚刚写x
Falsy<T> && x
应该是一个错误 - 如果已知左侧是假的,x
是无法访问的代码Maybe<T> && x
应该产生...什么?
我们知道结果 Maybe<T> && x
将是类型的虚假值 T
, , 或者 x
. 。它无法产生 Truthy<T>
(除非 T
== 的类型 x
在这种情况下,整个讨论就没有意义了)。我们称这个新类型为 Falsy<T> XOR Maybe<U>
.
应该遵守什么规则 Falsy<T> XOR Maybe<U>
是?
- 显然,您不能使用
T
在上面。如果该值是类型T
, ,它是虚假的,并且使用不安全。 - 您应该能够将它用作
Maybe<U>
, , 自从Falsy<T>
和Falsy<U>
有相同的行为 - 您不应该能够使用以下属性
U
, ,因为该值仍然可能是假的。 - 如果你在一个
if
测试,那么它应该成为Truthy<U>
在那个街区if
陈述
换句话说, Falsy<T> XOR Maybe<U>
是 Maybe<U>
. 。它遵循所有相同的规则。您根本不需要通过添加这个奇怪的东西来使类型系统变得复杂 XOR
类型,因为符合您需要的所有规格的类型已经存在。
这有点像给某人一个盒子并说“这要么是一个空的垃圾箱,要么是一整箱的可回收物”。您可以安全地将盒子里的东西倒进回收箱。