はじめに
最近、新規プロジェクトに配属されました。
if文を使って、値がundefined
の場合の条件式を今まで通り、!
を使って書いたところ、ESLintのエラーが発生しました。初めて見るエラーだったので、内容と解決方法について詳しく調べてみました。
- 言語:TypeScript
- ライブラリ:React
先にこの記事で伝えたいことをまとめておきます。
-
strict-boolean-expressions
エラーは、boolean型の値が必要な場所で、それ以外の型が指定された場合に発生する -
等価演算子を使うと
null == undefined // => true
と判定される -
x == null
と書いた場合、xがnull
かundefined
の場合という条件を表すことができる
問題
startTime
はDate | undefinrd型
をとるstateです。
startTime===undefined
の場合は早期リターンしたかったので、条件分岐を!
を使って以下のように書きました。
const [startTime, setStartTime] = useState<Date | undefined>()
if (!startTime) {
return
}
すると!startTime
に以下のようなESLintのエラーが発生しました。
そして保存すると、勝手に以下のように変換され、エラーが解消されました。
if (startTime == null) {
return
}
一連の流れでいくつかの疑問が浮かんだので、調べました。
疑問点
- このエラーの意味は何か
- なぜこれまでのプロジェクトではこのエラーが発生しなかったのか
- なぜ
startTime == null
でエラーが解消されるのか - ソースコードにはどのように書くか
このエラーの意味は何か
Unexpected nullable object value in conditional. An explicit null check is required.
今回発生しているESLintのエラーはTypeScript strict-boolean-expressions
です。
公式ドキュメントによると、このエラーは、boolean型の値が必要な場所で、それ以外の型が指定された場合に発生します。
例えば以下の時、エラーが発生します。numがundefined型をとる可能性があるからです。
// nullable numbers are considered unsafe by default
let num: number | undefined = 0;
if (num) {
console.log('num is defined');
}
以下のように修正することでエラーは解消されます。
// nullable values should be checked explicitly against null or undefined
let num: number | undefined = 0;
if (num != undefined) {
console.log('num is defined');
}
今回発生したエラーのように、条件式に!startTime
と書いてしまうと、startTimeがfalsy
のときtrue
と判定されます。つまり、本当はundefined
のときだけtrue
と判定したいのに、startTime
が0や空文字の場合にもtrue
と判定されてしまうということです。
このような曖昧な評価を防ぐためにstrict-boolean-expressions
は必要ということですね!
なぜこれまでのプロジェクトではこのエラーが発生しなかったのか
以前のプロジェクトを確認したところ、ESLintの設定でstrict-boolean-expressions
がOFFになっていました。
なぜstartTime == null
でエラーが解消されるのか
まずは使用されている文法について考えます。
等価演算子==
等価演算子では、型が異なっていても同じと見なすことがあります。
厳密には、値の型が異なる場合は型の変換ができないか試みてから値が等しいかを比較します。
// 等価演算子の場合
console.log(null == undefined); // => true
console.log(0 == "0"); // => true
// 厳密等価演算子の場合
console.log(null === undefined); // => false
console.log(0 === "0"); // => false
nullとundefindが等しいというのは感覚と反していて気持ち悪いと思いました😕
厳密等価演算子はよく使いますが、等価演算子は実務で初めて使いました。実際、今回と同じ使い方以外ではあまり使わないみたいですね。
よって、今回のようにstartTime == null
と書いた場合、startTimeがnull
かundefined
の場合という条件を表すことができます。
厳密等価演算子を使って表すと、startTime === null || startTime === undefind
という条件になるので、前者の方が簡潔でいいですね。
// 等価演算子の場合
if (startTime == null) {
return
}
// 厳密等価演算子の場合
if (startTime === null || startTime === undefind) {
return
}
ソースコードにはどのように書くか
先輩とも相談して、今回はstartTimeがundefindの場合という条件が、より正確に示せた方がわかりやすいだろうと判断して、以下のように修正することにしました。
簡潔に書くことと、伝わりやすいかのどちらを優先するのかは、チームによっても考え方が異なりそうですね。
const [startTime, setStartTime] = useState<Date | undefined>()
if (startTime === undefined) {
return
}
まとめ
-
strict-boolean-expressions
エラーは、boolean型の値が必要な場所で、それ以外の型が指定された場合に発生する -
等価演算子を使うと
null == undefined // => true
と判定される -
x == null
と書いた場合、xがnull
かundefined
の場合という条件を表すことができる
終わりに
プロジェクトによって設定ルールが異なるため、ソースコードの書き方が異なることを学びました。
strict-boolean-expressions
がONの時は、より厳密に判定条件を書く必要があるので注意していこうと思います!
また、null==undefined =>true
となることも学びました。なかなか納得しにくいですが覚えておきます!