yn2011's blog

技術メモ

TypeScript の演算子を整理する

TypeScript (というか最近のJavaScript)って、よく分からない演算子多くないですか? なんか色々な箇所で! とか ? とか見るので意味が分からないと結構ストレスになります。

自分があんまりキャッチアップしてないのが悪いんですが、馴染みがない/薄い演算子について使い方と登場の背景を整理した自分用のメモ。

Nullish Coalescing (??)

TypeScript 3.7~ で利用可能

こういうやつ

let x = foo ?? bar();

要はこれのシンタックスシュガー

let x = foo !== null && foo !== undefined ? foo : bar();

なぜ必要か

JavaScript を書いてフロントエンドの開発をすると上のような書き方を頻繁にするから。特にレガシーなフロントエンドのコードベースに少し手を入れる時なんかは、どの変数がいつ null や undefined になるか予想がつかない場合も多く、とにかく安全側に倒したかったりする。(例外が発生すると処理が止まるので)

ちなみに、単に x = foo || bar() と書くと、foo が 0 だったり空文字だったりすると false になって bar() が評価されるので困る。JavaScript 初心者が絶対に1度はハマる罠。?? だと NaN は回避されないみたいですが。

Optional Chaining(?)

TypeScript 3.7~ で利用可能

こういつやつ

let x = foo?.bar;

要はこれのシンタックスシュガー

let x = foo === null || foo === undefined ? undefined : foo.bar;

なぜ必要か

let x = foo && foo.bar とか書きたくないから。これもレガシー系フロントエンドの改修でありがちで、オブジェクトのプロパティと値がまったく予想できないので、こう書くしか無いパターンが多かった。とても助かる。がそもそも使わなくても良いのが望ましいとは思う。

Optional parameter (?)

こういうやつ

function foo(bar?: string) {
    ...
}

? を付けると string | undefined 型に推論される。これは厳密に演算子という区分なのかは分からないけど、まあそんな感じのものだと思ってる。

なぜ必要か

JavaScript の関数の引数は全て省略可能だが、引数が与えられない可能性があるのか、ないのかは区別できた方が嬉しい。そしてそうしないと型定義できない。

rest parameters (...)

こういうやつ

function foo (bar: string, ...baz: string[]) {
    ...
}

引数を無限に受け取ることを宣言できる。spread operators とはまた別だと思う。

なぜ必要か

無限に引数を受け取ること自体は従来からできて、 関数内から arguments で参照可能だった。ただし引数の宣言からはそれが読み取れなかったので分かりやすくなった。そしてそうしないと型定義できない。

Non-null assertion operator (!)

TypeScript 2.0~ で利用可能

こういうやつ

function foo(f?: Foo) {
    let bar = f!.bar; 
}

要はこれのシンタックスシュガー

let bar = (f as Foo).bar; 

なぜ必要か

外部のライブラリの型定義上 foo | undefined みたいに定義されているが、自分の実装上は絶対に undefined にならないから何とかしたいみたいなときに使うことが多い気がする。(自分が定義した型でこれが頻出するならそもそも型定義を見直した方がいいんじゃないか)

Short-Circuiting Assignment Operators (&&=, ||=, ??=)

TypeScript 4.0~ で利用可能

こういうやつ

a &&= b;
a ||= b;
a ??= b;

要はこれのシンタックスシュガー

a = a && b;
a = a || b;
a = a ?? b;

ちなみに、JavaScript に馴染みがなかったり、しばらく別の言語を書いていたりすると、「え? a && b とか a || b って bool 型しか返さないのでは?」となるかもしれない。 JavaScript の場合はそんなことはなく、短絡評価をして

  • a && b :a が false なら a を返す
  • a || b :a が true なら a を返す

という仕様になっている。この true か false かというのは、bool 型を期待しているわけではなく、JavaScript 流の解釈で、0, "", NaN, undefined, null ... 等は false でそれ以外が true みたいな感じに評価されるだけ。

非 bool 型の a, b で if(a && b) のように書くと、(if 文の条件式なので)返値が bool 型に変換されるのでそれっぽく振る舞っているだけという感じ。

なぜ必要か

正直、自分はあんまりこういうコードが必要になったことはない。まず、変数に対する再代入をすることがあまりない。JavaScript が一部継承している関数型プログラミングパラダイム的にも推奨してないと思う。

一方、競プロの問題を解く時は手続き的にサッと書きたいので、sum+=a とか sum /= a とかは頻繁に使う。でも a &&= b はあっても使うことないと思う。

あと、a ||= b するなら大抵は a ??= b の方が適切じゃないかと思う。上でも書いたけど、0 とか 空文字を false として扱って欲しいケースの方が少ない気がするので...