TypeScriptのルールをstrictにしたときにつまずいたこと
eslintをstrictにした
このブログはTypeScriptで開発しています.型定義が弱いところがあり,eslintのルールを見直してみようと思ったのがきっかけ.一つ一つ検討していくのは骨が折れるので,一旦strict系のルールを有効にしようとなりました.
strictにするにはtsconfig.jsonのcompilerOptionsのstrictフラグをtrueにします.
// tsconfig.json
{
"compilerOptions": {
"strict": true
}
}
この結果,めちゃくちゃeslintに怒られたので,その作業ログを残します.
strict-boolean-expressions
これは,booleanでない値を暗黙的にbooleanのように評価することを規制するルール.
例えばこんなコード.
const apple = {
color?: string
}
// Unexpected nullable string value in conditional. Please handle the nullish/empty cases explicitly.
if (apple.color) {
// なんかの処理
}
appleオブジェクトのcolorプロパティはstring | undefinedという状況で,truthyのときだけなにかの処理をしたいという分岐を書いています.
これのなにが問題かというと,colorプロパティがundefinedや空文字のとき(つまりfalsyとのき)に暗黙的な型変換が行われ,ifブロックの中が評価されるということ.いつtrueとしたくて,いつfalseとしたいかをbooleanで明確に書きなさいというルールがstrict-boolean-expresiionsだと理解しています.
ではどう書けばいいかというと,次のような感じ.
const apple = {
color?: string
}
if (apple.color !== undefined) {
// なんかの処理
}
undefinedでないときのみ,ifブロックの中が評価されるという分岐にしました.これでエラーは消えます.空文字の場合もifブロック内を評価してほしいときは,さらに条件を追加する必要があります.
多少コード量が増えますが,型安全性は増しました.
参考:Falsy値を比較せずにそのまま判定に使うことはやめよう
補足:truthyとfalsy
ちょっとここでtruthyとfalsyについてまとめておきます.
falsyな値
string型の空文字''number型の0number型のマイナスゼロ-0number型のNaNbigint型の0nboolean型のfalseobject型のdocument.allnullundefined
参考:Falsy (偽値) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
filterとsort
文字列の配列からundefinedを取り除き,ソートしたいという処理でエラーがでました.
const array = ['hoge', undefined, 'fuga', undefined]
const newArray = array
.filter((el) => el) // ここでundefinedを消したかった
.sort((a, b) => a > b) // ここで降順に並べたかった
filterメソッドのコールバック関数の返り値はboolean
'hoge'や'fuga'はtruthyで,undefinedはfalsyなので,これで行けると思いましたが,上のstrict-boolean-expressionで怒られました.
sortメソッドのコールバック関数の返り値はnumber
「aやbがundefinedのことがあるよ」と怒られました.一つ前のプロトタイプチェーンでfilterをしているので,undefinedはないはずなのに....また,「booleanであるa > bではなく,numberを返しなさい」と怒られました.
修正方法
以下のように修正しました.
const array = ['hoge', undefined, 'fuga', undefined]
const newArray = array
.filter((el): el is string => typeof el === 'string')
.sort((a,b) => b.localeCompare(a))
is演算子を使って,filterメソッド適用後はstring型のみになることを明示しました.Stringオブジェクトの組み込みメソッドであるlocaleCompareでnumberを返すように変更しました.