为什么和&& operator生成第二个操作数的类型

时间:2012-10-02 15:50:07

标签: types typescript

TypeScript规范在§4.15.6中说明了&&运算符:

  

&&运算符允许操作数为任何类型,并产生与第二个操作数相同类型的结果

在Javascript中,&&运算符返回第一个操作数(如果它是假的),否则返回第二个操作数(see ECMA-262 §11.11)。

这意味着如果左操作数是假的,&&将返回一个与左操作数的类型匹配的值。例如,

typeof ( false && {}      ) === "boolean" // true
typeof ( ''    && 1       ) === "string"  // true
typeof ( null  && "hello" ) === "object"  // true
typeof ( NaN   && true    ) === "number"  // true

根据上面引用的规则,Typescript会错误地预测上述表达式的类型为ObjectNumberString和{{ 1}},分别。

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

2 个答案:

答案 0 :(得分:23)

长话短说,这里没有解决方案让所有人满意。

考虑这个常见的习语:

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
}

放弃并决定'地址'是'任何'是非常蹩脚的,因为客户和地址之间没有最佳的共同类型。

在大多数情况下&&使用运算符,匹配类型,或&&正在以如上所述的价值合并方式使用。在任何一种情况下,返回右操作数的类型都会为用户提供预期的类型。

虽然此时类型安全在技术上已经崩溃,但它不会以可能导致错误的方式执行此操作。您要么测试结果值的真实性(在这种情况下类型或多或少不相关),或者您将使用假定的右操作数进行某些操作(上面的示例同时执行这两个操作)。

如果我们查看你列出的例子并假装左操作数是不确定的truthy或falsy然后尝试编写将对返回值进行操作的合理代码,它会变得更加清晰 - 你可以 与'false&& {}'尚未进入'任何'参数位置或真实性测试。


<强>附录

由于有些人不相信上述内容,所以这里有不同的解释。

让我们暂时假装TypeScript类型系统添加了三种新类型:Truthy<T>Falsy<T>Maybe<T>,表示T类型的可能的真值/假值。这些类型的规则如下:

  1. Truthy<T>的行为与T
  2. 完全相同
  3. 您无法访问Falsy<T>
  4. 的任何属性
  5. 类型Maybe<T>的表达式,当用作if块中的条件时,将成为同一Truthy<T>块的主体中​​的if和{{ 1 {}在Falsy<T>
  6. 这可以让你做这样的事情:

    else

    到目前为止相当不错。现在我们可以弄清楚&amp;&amp ;;的类型规则是什么。操作

    • 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>类型来复制类型系统,因为适合您所需的所有规范的类型已经存在。

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

答案 1 :(得分:1)

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

来自TypeScript 2.0 release notes

// 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