こんにちは!TypeScriptの世界へようこそ。
日々、フロントエンドからバックエンドまでバリバリとコードを書いていると、「配列の何番目かを取り出す」という処理は、もう空気のように当たり前に書くものですよね。
でも、他のプログラミング言語(例えばJavaやPython、C#など)からTypeScriptに入ってきた人が、必ずと言っていいほど最初に踏む「見えない地雷」があります。それが、「配列のインデックスアクセスと `undefined` の罠」です。
ここをクリアすれば、TypeScriptの型安全性の本質がグッと見えてきて、コードの信頼性が劇的に変わりますよ。今日は優しく、かつ深く、このテーマを一緒にマスターしていきましょう!
—
1. 幻想と現実:なぜ `array[i]` は `undefined` を返すのか?
まずは、こんなコードを想像してみてください。
const fruits: string[] = [“apple”, “banana”, “orange”];
// 0番目の「apple」を取り出したい!
const firstFruit = fruits[0];
株価や文字数を調べようと、toUpperCase() を使ってみます。
console.log(firstFruit.toUpperCase()); // 動く……ように見えますよね?
他の言語の感覚だと、「`fruits` は `string` の配列なんだから、何番目を取り出しても中身は絶対に `string` だろう」って思いますよね。
しかし、TypeScriptのデフォルトの挙動(あるいは他の多くの言語の現実)では、ここに大きな落とし穴があります。もし、存在しないインデックスを指定したらどうなるでしょうか?
const dangerousFruit = fruits[99]; // そんな要素はない!
console.log(dangerousFruit); // 実行時は当然 undefined になる
もしTypeScriptが「ここには絶対に `string` があるよ!」と嘘をつき続けたら、存在しない 99番目の要素に対して `.toUpperCase()` を呼んだ瞬間、アプリは「TypeError: Cannot read properties of undefined」で盛大にクラッシュしてしまいます。
TypeScriptは「コンパイル時にバグを根絶する」ための言語です。だからこそ、配列の要素にアクセスしたとき、型システムはこう囁きます。
> 「ねえ、本当にそこにそのデータはある? もしかしたら `undefined` かもしれないよ?」
—
2. デフォルトの甘えを断ち切る:`noUncheckedIndexedAccess` という最強の盾
実は、TypeScriptのデフォルト設定では、利便性(あるいは歴史的経緯)のために、配列のインデックスアクセスは「存在するはずだ」と楽観視(Bail out)して `string`型を返してしまいます。
これを厳格にし、「配列の何番目かにアクセスした結果は、常に `undefined` の可能性がある」と正しく強制する設定が、tsconfig.jsonのこのフラグです。
{
“compilerOptions”: {
“noUncheckedIndexedAccess”: true
}
}
このオプションを有効にした瞬間、あなたのTypeScriptライフは一変します。先ほどのコードを見てみましょう。
const fruits: string[] = [“apple”, “banana”, “orange”];
const firstFruit = fruits[0];
// 【型推論の結果】
// string | undefined
// エラーになる!
// ❌ ‘firstFruit’ は ‘undefined’ の可能性があります。
console.log(firstFruit.toUpperCase());
「えっ、エラーになっちゃった!どうすればいいの?」って焦りましたか?
素晴らしい!その焦りこそが、TypeScriptの型安全性を理解できた証拠です。コンパイラが「`undefined` の可能性をちゃんとケアしなさい!」と優しく、しかし厳しく教えてくれているんですね。
—
3. 現場で使える!安全なアクセスと値の取り出し方
じゃあ、`noUncheckedIndexedAccess: true` にした世界で、どうやって安全に配列を扱えばいいのでしょうか? 実務で即座に使える3つのアプローチを伝授しますね。
アプローチ A:ガード節(条件分岐)で絞り込む
王道かつ最も確実な方法です。「もし値が存在するなら」というチェックを挟みます。
const fruits: string[] = [“apple”, “banana”, “orange”];
const firstFruit = fruits[0];
// ガード節で undefined でないことを確定させる
if (firstFruit !== undefined) {
// このブロックの中では、firstFruit は安全に string 型として扱われます!
console.log(firstFruit.toUpperCase()); // 完ぺき!
}
アプローチ B:オプショナルチェーニング (`?.`) を使う
「値があればメソッドを実行し、なければ `undefined` をそのまま返す」というスマートな書き方です。
const fruits: string[] = [“apple”, “banana”, “orange”];
// 値があれば toUpperCase() を実行、なければ全体が undefined になる
const upper = fruits[0]?.toUpperCase();
// upper の型は string | undefined になるので安全
console.log(upper);
アプローチ C:Null合体演算子 (`??`) でデフォルト値を設定する
「もしその要素がなかったら、代わりに別のデフォルト値を使いたい」という場合は、`??`(Nullish coalescing)が最高の相棒になります。
const fruits: string[] = [“apple”, “banana”, “orange”];
// 99番目がなければ “unknown” を採用する
const missingFruit = fruits[99] ?? “unknown”;
// missingFruit の型は string に確定する!(undefined が消える)
console.log(missingFruit.toUpperCase()); // “UNKNOWN” と出力される
—
4. やっちゃいけない!初心者が陥る「型アサーション」のアンチパターン
ここで、よくある初心者の「間違い」についても触れておきます。エラーが鬱陶しいからといって、次のようなコードを書きたくなってしまう衝動に駆られることがあります。
// ⚠️ やってはいけない例(型アサーションの乱用)
const firstFruit = fruits[0] as string;
// 「私はここに string があると知っているんだ!」と TypeScript に嘘をつかせる
console.log(firstFruit.toUpperCase());
`as string`(型アサーション)を使うと、コンパイラを無理やり黙らせることができます。しかし、これは「安全ベルトを自ら外して時速100キロでドライブする」ようなものです。もし配列が空(`[]`)だった場合、容赦なくアプリはクラッシュします。
TypeScriptの恩恵を最大限に受けるために、コンパイラと敵対するのではなく、コンパイラと「対話」しながらコードを書くようにしましょうね。
—
まとめ:ここをクリアすれば、もう怖くない!
いかがでしたでしょうか?
- 配列のインデックスアクセス (`array[i]`) は、常に `undefined` を返すリスクを孕んでいる。
- `noUncheckedIndexedAccess: true` を有効にすることで、そのリスクを型システムで完全に捕捉できる。
- `if`文、`?.`、`??` を使い分けて、優しく安全にデータを扱おう。
この「配列と `undefined` の関係性」を腑に落とすことができれば、あなたの書くTypeScriptコードの堅牢性は見違えるほど跳ね上がります。
ここをクリアできれば、TypeScriptの基本はもうバッチリマスターです!自信を持って、次のステップへ進んでいきましょう。それでは、快適な型安全ライフを!