39

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 会错误将上述表达式的类型分别预测为ObjectNumber和。StringBoolean

我错过了什么吗?是否有充分的理由使&&表达式的类型与第二个操作数的类型匹配?结果类型不应该表现得像||运算符一样,返回两个操作数中最好的公共类型,Any如果没有最好的公共类型?

4

2 回答 2

29

长话短说,这里没有让每个人都满意的解决方案。

考虑这个常见的成语:

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。这些类型的规则如下:

  1. Truthy<T>行为完全一样T
  2. 您无法访问任何属性Falsy<T>
  3. 类型的表达式Maybe<T>,当用作块中的条件时,在同一块的主体中​​变为 a ,在块中if变为 aTruthy<T>ifFalsy<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要么是类型的假值,要么Tx。它不能产生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类型来使类型系统复杂化,因为已经存在适合您需要的所有规范的类型。

这有点像给某人一个盒子并说“这是一个空的垃圾箱,或者是一整箱可回收物”。您可以安全地将盒子中的物品清空到回收箱中。

于 2012-10-02T16:55:39.363 回答
2

语言规范没有维护,在这种情况下是错误的。运算符的结果类型&&是右侧的类型和左侧的假值的并集。

来自TypeScript 2.0 发行说明

// Compiled with --strictNullChecks
declare function f(x: number): string;
let x: number | null | undefined;
// ..
let b = x && f(x); // Type of b is string | 0 | null | undefined
于 2021-01-08T13:51:25.580 回答