概要
つい最近TypeScriptでコードを書いていて、条件演算子(?:)とNull合体演算子(??)の違いに引っかかったので忘れないようにそれぞれの違いと、どこに引っかかったのかをメモした記事です。
条件演算子(?:)
条件演算子は以下の形式で使用され、条件が真値であった場合に左辺を、条件が偽値であった場合に右辺を実行します。
条件 ? 左辺 : 右辺
JavaScriptで偽値として扱われるのは次の値です。
- false
- 0
- 0n(BigInt)
- 負の値
- 空文字列("")
- null
- undefined
- NaN
そのため、上記の値を条件として利用した場合は全て右辺が実行されます。
false ? "true value" : "false value"
// false value
0 ? "true value" : "false value"
// false value
"" ? "true value" : "false value"
// false value
null ? "true value" : "false value"
// false value
undefined ? "true value" : "false value"
// false value
Null合体演算子(??)
次にNull合体演算子(??)についてです。
Null合体演算子は以下の形で使用され、左辺が null
または undefined
の場合に右辺を返し、それ以外の場合に左辺を返します。
左辺 ?? 右辺
自分が引っかかったのがまさにここで、一部の書き方で利用する場合に同様の動作をするため、Null合体演算子(??)は条件演算子(?:)の記述と同じ条件で同じように動作すると勘違いしていました。
そのため、それぞれの演算子に null
または undefined
を利用する場合とそれ以外の場合で動作が異なることに気がつかず、想定外の動作が発生した時にはしばらく唸ってました。
もう少し具体的に見ていきます。
上記の説明の通り、Null合体演算子は左辺が null
または undefined
の場合に右辺を返します。
そのため、以下の記述はどちらもvalue
がundefined
の場合はfalse value
を返し、それ以外の場合はその値を返すという動作をします。
const value = undefined;
// string | undefined
const result1 = value ? value : "false value";
// false value
const result2 = value ?? "false value";
// false value
この場合は、結果的にNull合体演算子(??)で条件演算子(?:)の記述を省略した書き方になっています。
では、Null合体演算子(??)が条件演算子(?:)と同じ条件で同じように動作するだと勘違いしたまま次のようなコードを書くとどうなるでしょうか?
const number = Number(process.env.NUMBER) ?? 1
// process.env.NUMBERは string | undefined
// numberを利用した処理
上記のコードでは、number
に必ず数値が入ることを想定しています。
Null合体演算子(??)が条件演算子(?:)と同じ条件で同じように動作する場合、process.env.NUMBER
が偽値の場合Number(process.env.NUMBER)
も偽値(NaN
)のためnumber
は1
となり、以降の処理でも数値として扱われるため想定通りに動作します。
ただし、実際にはNull合体演算子(??)は 左辺が null または undefined の場合のみ右辺を返す演算子なので、左辺がNaN
の場合はそのままNaN
を返します。
そのため、以下のnumber
を利用した処理で想定していない動作をすることになります(というか実際になりました)。
ちなみに以下のように書くと、想定している通りに処理ができます。
const number = Number(process.env.NUMBER || "1")
// numberを利用した処理
このように一部の状況では省略記法のように書くことができても根本の動作が違う場合、気がつかずにバグを生む可能性があります。
なので、新しいものを使うときはまずドキュメントを読みましょうという自戒を込めた記事でした。