web-dev-qa-db-ja.com

&&演算子が第2オペランドの型を生成するのはなぜですか

TypeScript仕様では、§4.15.6で&&演算子について次のように述べています。

&&演算子を使用すると、オペランドを任意の型にすることができ、2番目のオペランドと同じ型の結果が生成されます。

Javascriptでは、&&演算子は、偽の場合は最初のオペランドを返し、それ以外の場合は2番目のオペランドを返します( ECMA-262§11.11を参照 )。

つまり、左のオペランドが偽の場合、&&は左のオペランドのタイプに一致する値を返します。例えば、

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

TypeScriptは、上記のルールに従って、上記の式のタイプを誤って予測してObjectNumberStringBoolean、それぞれ。

私は何かが足りないのですか? &&式の型を第2オペランドの型と一致させる正当な理由はありますか?結果の型は||演算子のように動作し、2つのオペランドの最も一般的な型を返し、最も一般的な型がない場合はAnyを返す必要がありますか?

31
Peter Olson

簡単に言えば、ここには誰もが喜ぶ解決策はありません。

この一般的なイディオムを考えてみましょう。

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
}

CustomerとAddressの間に最適な一般的なタイプがないため、あきらめて「address」を「any」と決定するのはかなり難しいでしょう。

&&演算子が使用されるほとんどの場合、タイプalreadyが一致するか、&&が上記のような値の合体方法で使用されています。いずれの場合も、右のオペランドの型を返すと、ユーザーは期待される型になります。

型安全性はこの時点で技術的に故障していますが、エラーが発生する可能性のある方法では故障していません。結果の値の真偽をテストするか(この場合、型は多かれ少なかれ無関係です)、または何らかの操作に推定右オペランドを使用します(上記の例では両方を実行します)。

あなたがリストした例を見て、左のオペランドが不確定に真実または偽であると偽って、戻り値を操作する正気のコードを書き込もうとすると、それははるかに明確になります-できることはあまりありませんdowith'false && {} 'は、まだ' any '引数位置または真正性テストに入っていません。


補遺

上記に納得できない方もいらっしゃるので、別の説明をさせていただきます。

TypeScript型システムが3つの新しい型を追加したとしましょう:Truthy<T>Falsy<T>、およびMaybe<T>は、タイプTの可能な真偽の値を表します。これらのタイプのルールは次のとおりです。

  1. Truthy<T>Tとまったく同じように動作します
  2. Falsy<T>のプロパティにはアクセスできません
  3. タイプMaybe<T>の式は、ifブロックの条件として使用されると、同じifブロックの本体ではTruthy<T>になり、elseブロックではFalsy<T>になります。

これにより、次のようなことができます。

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テストで使用する場合は、そのifステートメントのブロックでTruthy<U>になるはずです。

言い換えれば、Falsy<T> XOR Maybe<U>isMaybe<U>。それはすべて同じルールに従います。必要なすべての仕様に適合する型がすでに存在するため、この奇妙なXOR型を追加することにより、ここで型システムを複雑にする必要はまったくありません。

これは、誰かに箱を渡して「これはゴミの空の箱か、リサイクル可能な箱一杯のどちらかです」と言うのと少し似ています。ボックスの中身を安全にごみ箱に入れることができます。

25
Ryan Cavanaugh