长话短说,这里没有让每个人都满意的解决方案。
考虑这个常见的成语:
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
}
放弃并决定“地址”是“任何”是很蹩脚的,因为客户和地址之间没有最好的通用类型。
在使用 && 运算符的大多数情况下,类型已经匹配,或者 && 正在以类似于上面的值合并方式使用。在任何一种情况下,返回正确操作数的类型都会为用户提供预期的类型。
虽然此时类型安全在技术上已经崩溃,但它并没有以可能导致错误的方式这样做。要么您要测试结果值的真实性(在这种情况下,类型或多或少无关紧要),要么您将使用推定的右操作数进行某些操作(上面的示例同时执行这两种操作)。
如果我们查看您列出的示例并假装左操作数不确定是真还是假,然后尝试编写可以对返回值进行操作的理智代码,它会变得更加清晰-您无能为力'false && {}' 尚未进入“任何”参数位置或真实性测试。
附录
由于有些人不相信上述内容,这里有一个不同的解释。
让我们暂时假设 TypeScript 类型系统添加了三种新类型:Truthy<T>
、Falsy<T>
和Maybe<T>
, 表示 type 可能的真值/假值T
。这些类型的规则如下:
Truthy<T>
行为完全一样T
- 您无法访问任何属性
Falsy<T>
- 类型的表达式
Maybe<T>
,当用作块中的条件时,在同一块的主体中变为 a ,在块中if
变为 aTruthy<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
它的属性。如果该值为 type T
,则它是虚假的,并且使用起来不安全。
- 您应该能够将其用作
Maybe<U>
, sinceFalsy<T>
并且Falsy<U>
具有相同的行为
- 您不应该使用 的属性
U
,因为该值仍然可能是虚假的。
- 如果您在
if
测试中使用它,那么它应该成为该语句Truthy<U>
块中的aif
换句话说,Falsy<T> XOR Maybe<U>
是 Maybe<U>
。它遵循所有相同的规则。您根本不需要通过添加这种奇怪的XOR
类型来使类型系统复杂化,因为已经存在适合您需要的所有规范的类型。
这有点像给某人一个盒子并说“这是一个空的垃圾箱,或者是一整箱可回收物”。您可以安全地将盒子中的物品清空到回收箱中。