TypeScript 5.6の"Disallowed Nullish and Truthy Checks"

2024/09/09に公開

TypeScript 5.6に入る新機能、Disallowed Nullish and Truthy Checksが気になったので少し手を動かして調べてみました。心の中にしまっておくのも勿体無いので皆さんに共有しておきます。

以下はまとめですが、nullishの扱いについてはほとんど同じ話の繰り返しになるので本記事では触れません。

  • 構文的にtruthyだとわかる値を条件節に入れるとエラーになる
  • 同じく構文的にnullishでないわかる値を??の左辺に渡すとエラーになる
  • あくまで構文上の情報しか使っていないので、型情報は使っていないので変数に入れて渡すと通る
  • falsyな値についても同様のチェックが入る

本題

truthy/falsyのおさらい

JavaScriptにはtruthy/falsyという概念があります。どんな値も、ifwhileなどの条件節に与えるとtruefalseに変換されます。trueに変換される値をtruthyfalseに変換される値をfalsyと呼びます。

問題

どんな正規表現もtruthyなので、以下のifのボディは常に実行されてしまいます。

if (/0x[0-9a-f]/) {
    // something to do
}

実装者はもしかしたらtestメソッドを使い忘れたのかも知れませんが、これまでのTypeScriptコンパイラーはこのコードを通すので、実装者は実行するまでこの事実に気づけませんでした。

他にも、関数はtruthyなので、<==>にタイポして関数にしてしまうとやはりifが常に実行されてしまいますが、コンパイル時には気づけません。

しかし、TypeScript 5.6からは上記のコードはコンパイル時にエラーになります。コンパイラは/0x[0-9a-f]/が正規表現であることを構文から理解できるので、その情報を元に「こいつはいつもtruthyだよ」とエラーを出してくれます。

変数に入れて渡すと通る

注意点として、あくまで構文しか見ていないので変数に入れて渡すとエラーになりません。

const regex = /0x[0-9a-f]/
if (regex) {}

TypeScriptにはRegExp型があるのでエラーにしてくれるかと思いきや、構文からは判断できないのでエラーになりません。
また、配列もtruthyなのでif ([]) {}はエラーになりますが、

const arr = []
if (arr) {}

は通ります。

例外として、関数は変数に入れてもエラーになります。

if (x => 0) {} // error

const f = (x: number) => 0

if (f) {} // error

ただ、if (f) {}はこれまでのバージョンでもエラーだったようです。エラーメッセージはThis condition will always return true since this function is always defined. Did you mean to call it instead?(2774)となっており、今回導入されたエラーとはコードが違います(これは2774, 今回導入されたのは2872)。他方、if (x => 0) {}のエラーコードはちゃんと2872になっています。

ちなみに、アナウンスではtruthyしかチェックしないかのようなタイトルになっていましたが、falsyに対しても同様のチェックが入るようで、例えばif (undefined) {}もエラーになりました。

今回紹介しなかったnullishチェックの方も同様の性質を持っています。あまりハマる人もいないかも知れませんが念の為の注意喚起でした。
最後にplaygroundを置いておきます。ではでは
https://www.typescriptlang.org/play/?ts=5.6.1-rc#code/JYMwBAFA2guglGA3gXwFCoMYHsB2BnAFzAEMAuMAVxwGscsB3HWMAXjFnVEmIRU-AgB6AAwAPKMIC0ATmKSQMQbzSZchMACcApgHMto1mBHips+Yv6Rte0cssQDLAHxhhd1fiLg2D8jgoAtgBGWhoIzq72IHZAA

Discussion