No.
2022-03-23
  • Jan
  • Feb
  • Mar
  • Apr
  • May
  • Jun
  • Jul
  • Aug
  • Sep
  • Oct
  • Nov
  • Dec
  • Sun
  • Mon
  • Tue
  • Wed
  • Thu
  • Fri
  • Sat
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

やったこと

Falsy値についての面白い記事を見つけたので、自分なりにまとめる
Falsy値を比較せずにそのまま判定に使うことはやめよう

Falsy 値とは

MDN Falsy

偽値 (falsy または falsey な値) は、 Booleanコンテキストに現れたときに偽とみなされる値です。

スクリーンショット 2022-03-23 17 38 14

Boolean コンテキストとは

wiki booleanコンテキスト

if文の条件式に渡されたオブジェクトは、自動的に真偽値に変換されます。このように、暗黙に真理値に変換される文脈は、ブーリアンコンテキストとよばれます。

Falsy 値は実際どう評価されているか

ECMAScript if statement

if ( Expression ) Statement else Statement
  1. Let exprRef be the result of evaluating Expression.
  2. Let exprValue be ToBoolean(? GetValue(exprRef)).
  3. If exprValue is true, then a. Let stmtCompletion be the result of evaluating the first Statement.
  4. Else, a. Let stmtCompletion be the result of evaluating the second Statement.
  5. Return ? UpdateEmpty(stmtCompletion, undefined).

ToBoolean()という箇所で「その値が Falsy 値なのかどうか」という関心が作用する

ToBoolean 抽象操作

ECMAScript ToBoolean

抽象操作は訳語であり、原典では”Abstract Operations”と記載されている

これは、JavaScript の標準的な関数による様々な「型変換」について変換の仕様を定めている

1997 年当時の仕様書では、この節は”Abstract Operations”とはなっておらず、単に”Type Conversion”とされていました。初版には次のように記載されています。

The ECMAScript runtime system performs automatic type conversion as needed

最新の ECMAScript の仕様では次のように記載されています。

The ECMAScript language implicitly performs automatic type conversion as needed

最新版では”implicitly”(暗黙的)のニュアンスが追記されていることがわかります。そう、TypeScript による explicitly(明示的)な型付けが進んでいるこの時代において、TypeScript が JavaScript に変換されて動作する以上「TypeScript を利用する explicitly を心がける開発者」であっても、言語仕様として implicitly な型変換が行われる可能性を常に考慮せざるを得ない

Booleanコンテキストとは「ToBoolean 抽象操作が行われる状況」のことを指す

Falsy値を比較せずにそのまま判定に使うことはやめる

「false以外の Falsy 値を Boolean コンテキストに渡すことをやめよう」と言い換えることができる

function getLabel(name: string): string {
  if (!name) {
    return "名無しさん";
  }
  return `${name}さん`;
}
function getLabel(name: string): string {
  if (!name.length) {
    return "名無しさん";
  }
  return `${name}さん`;
}

上記に二つの例がある

前者!nameと後者!name.lengthは同じ結果となりますが、Boolean コンテキストの解釈が異なります。nameはstring型の引数であるため、 ToBoolean 抽象操作にてfalseとなりうるのは”“(空文字列)のときだけです。

そして、name.lengthという式にした場合、lengthはnumber型の値を返すため(根拠1, 2)number 型の値が ToBoolean 抽象操作によってfalse となりうるのは0, -0, NaNのときであることから、!name.lengthについては”“.lengthの値が0になるためにfalseとなります。

function getLabel(name: string): string {
  if (name === "") {
    return "名無しさん";
  }
  return `${name}さん`;
}

そのため、上のように書いていく方針がいい

Boolean コンテキストを成す ToBoolean 抽象操作は、あらゆる値をtrueかfalseに「暗黙的に」変換しています。

筆者の結論

「TypeScript は暗黙的な型変換を避けるようなデザインに寄っているのだから、ToBoolean 抽象操作についてエラーとならなかったとしても言語の設計意図を汲み、Boolean コンテキストにおいてはfalse以外の Falsy 値をそのまま渡すべきではない」