【JS】findIndexとNullishCoalescingを同時に使うと危険!
こんにちは!NullishCoalescingのスペルを毎回ググるMizutani(@sirycity)です。フロントエンド系の単語って難しいよね。moleculesとかも毎回ググる。
今日はそんなNullishCoalescingについて、findIndexと合わせると危険だよ!って記事です。
findIndexとは
条件に一致するのが何番目かってメソッドです。例えば干支一覧みたいな配列を作って…
const ETOS = [
'ね',
'うし',
'とら',
'う',
'たつ',
'み',
'うま',
'ひつじ',
'さる',
'とり',
'いぬ',
'い',
]
ここで羊は何番目だっけ?みたいな時にこう使う。
const i = ETOS.findIndex(E => E === 'ひつじ')
// 7
そうすると7番目だ!ってわかる。こんだけ。
NullishCoalescingとは
左側がnullかundefinedなら右側にするってやつ。こんだけ。
const foo = null ?? 'あ'
// あ
const bar = 0 ?? 'あ'
// 0
得てしてNullishCoalescingはデフォルト値の設定によく使われます。なんか値入ってたらその値そのまま使うけど(左側)、何もなかったらデフォルトの値(右側)使うよーみたいな感じ。
両方セットで使うとハマる罠
Q1(ハマらないパターン)
こいつらを組み合わせてみましょう。たとえば3文字の干支は何番目かを表示するけどいなかったら末尾の番号を表示みたいなコードを書くと…
const threeLetterEtoIndex =
ETOS.findIndex(E => E.length === 3) ?? ETOS.length - 1
// 7
結果は7。7番目の「ひつじ」が3文字なので正解ですね〜
今回はたまたま3文字の干支がいましたが、いなかった場合はNullishCoalescingが発動して右側のETOS.length - 1
、つまり末尾の番号が表示されることが想定できます。設問どおりですね。
Q2(ハマるパターン)
ちょっと設問を変えて4文字の干支は何番目かを表示するけどいなかったら末尾の番号を表示にしてみましょう。3が4になっただけ。どうなるかな?
const fourLetterEtoIndex =
ETOS.findIndex(E => E.length === 4) ?? ETOS.length - 1
// -1
…ん?
あれ?
まいなす…いち?
4文字の干支はいません。なのでNullishCoalescingによって右側の11になりそう…です。が、11。
ハマる原因
原因はシンプル。いたってシンプル。いいですか、言いますよ。
findIndexは空振ると-1が返ってきます。nullではありません。
もう1回言いますよ。
空振ると-1が返ってきます。nullではありません。
これだけの話です。空値が-1になるのでNullishCoalescingに引っかかりません。おしまい。
なんでや
仕様なので仕方ないですね。勿論類似のメソッドはそんなこともなく、例えばfind
は空振るとundefinedです。えっnullじゃないの?と思いますがそれはまた別の話、NullishCoalescingに引っかかることが大事なんです
const fourLetterEto = ETOS.find(E => E.length === 4) ?? ETOS[ETOS.length - 1]
// い
//
// ↑ちゃんと最後の干支になった
他にもindexOf
とかも同じ仕様(-1が空値)ですが、indexOfはincludesが出てからはほぼ使わないので問題はナシ
対策
しょうがないですね。一番シンプルな解決策は-1をnullにすること。コード超きたないけどこんな感じ。
const tmp = ETOS.findIndex(E => E.length === 4)
const tmp2 = tmp === -1 ? null : tmp
const fourLetterEtoIndex = tmp2 ?? ETOS.length - 1
// 11
さいごに
JavaScriptのメソッドはなんか微妙にアレなやつが多い気がしないでもないですが、まあ歴史の長い言語だし仕方ないですね。JSを我が子のように思えば少々出来が悪くたって許せるものです。以上。