はじめに
arr[i] でインデックスアクセスをすると、TypeScript は「必ず値が存在する」前提で型を推論します。でも実際には範囲外のインデックスを渡せば undefined が返ってくる——この型と実態のズレに困って、解決策を調べているうちに Array.prototype.at() というメソッドの存在を初めて知りました。
他の人の .at() 紹介記事を読むと、だいたい「負のインデックスが使えて便利」という話に終始しています。それ自体は確かに便利なのですが、個人的には TypeScript での型推論が正確になる 点の方がずっとインパクトが大きかったので、そちらを中心に整理します。
arr[i] との違い——型推論が変わる
const arr: string[] = ["a", "b", "c"]
arr[0] // 型: string
arr.at(0) // 型: string | undefined
arr[0] は TypeScript 的に「必ず存在する」として扱われます。実行時に undefined が返る可能性があるのに、型には表れません。
一方、arr.at(0) は string | undefined と推論されます。「存在しないかもしれない」というリアルな可能性が型に出てくるわけです。
実用例:?? null パターン
これが地味に役立つのがこういうケース。
// ❌ result[0] は T と推論される(null が型に入らない)
return result[0] ?? null
// ✅ result.at(0) は T | undefined と推論される
return result.at(0) ?? null // T | null として正しく推論される
?? null は「undefined や null のときは null を返す」という意図で書くことが多いですが、result[0] だと型上は T なので null が戻り値型に入らず、型と実態がずれます。result.at(0) に変えるだけで型推論が正確になります。
noUncheckedIndexedAccess という選択肢もある
TypeScript の tsconfig.json に noUncheckedIndexedAccess: true を設定すると、arr[0] も string | undefined と推論されるようになります。
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}
const arr: string[] = ["a", "b", "c"]
arr[0] // noUncheckedIndexedAccess: true のとき → string | undefined
ただし、これは 全ての配列アクセスに影響する ので、既存コードへの影響が大きくなりがちです。ある日突然有効にすると型エラーが大量発生する可能性も。
arr.at() は「この場所だけ安全に書きたい」というピンポイントな用途に向いていて、プロジェクト設定を変えずに済みます。
負のインデックスも便利
こちらは知名度が高いですが一応。
arr.at(-1) // 末尾の要素 = "c"
arr[-1] // undefined(JavaScript では負のインデックスは無効)
arr[arr.length - 1] の代替として使えます。こちらも戻り値が string | undefined になるので、存在チェックを忘れずに。
まとめ
-
arr.at(i)はarr[i]と異なり、戻り値がT | undefinedと推論される -
?? nullなど「undefinedを考慮したい」場面では.at()の方が型が正確になる - プロジェクト全体に効かせたい場合は
noUncheckedIndexedAccess: trueという手もあるが、既存コードへの影響は大きい - 末尾要素の取得(
arr.at(-1))も副産物として使える