TypeScriptを書き始めて一番最初に「あれ?」とつまずくポイント、それが「空の配列 `[]` を作ったあとに `push` しようとしたら怒られた」という現象ではないでしょうか。
他の言語(JavaScriptやPythonなど)の感覚でいると、「とりあえず空の箱を用意して、あとからデータを詰め込んでいけばいいや」と思いますよね。でも、TypeScriptはそれを少し厳しく(そして親切に!)止めてきます。
ここをクリアすれば、TypeScriptの型推論の仕組みがグッと身近になりますよ。今日は、なぜ空配列が「ただの `any[]`」になってしまうのか、そしてどうやってスマートに回避すればいいのかを、一緒に紐解いていきましょう!
—
1. 犯人は誰だ? 空配列 `[]` が `any[]` に化ける瞬間
まずは、よくあるこんなコードを見てみてください。
// 空の配列を作りました
const userIds = [];
// あとから文字列を入れようとする
userIds.push(“user-001”); // あれ…?
このコード、JavaScriptなら何の問題もなく動きます。しかし、TypeScriptのコンパイラ(型をチェックしてくれる仕組み)は、ここで少し困ってしまうのです。
TypeScriptの脳内を覗いてみよう
あなたが `const userIds = [];` と書いた瞬間、TypeScriptのコンパイラはこう考えます。
> 「ふむ、今この瞬間、この配列の中身は 空っぽ だ。これから数字が入るのか? 文字列が入るのか? それともオブジェクトが入るのか? 私にはまだ予想がつきない……。
> しょうがない、中身が完全に不明だから、なんでも入れられるように `any[]`(なんでもありの配列) という仮のラベルを貼っておこう」
この瞬間、`userIds` の正体は `any[]` に決定されます。
なぜ `any[]` だとダメなの?
「なんでも入れられるなら便利じゃん!」と思いますよね。ここにTypeScriptの優しさ(厳しさ)があります。
TypeScriptの最大の目的は、「コードの安全性を守ること(バグを未然に防ぐこと)」です。
`any` 型を許可してしまうと、「型安全(TypeScriptの恩恵)」がすべて無効化されてしまいます。そのため、近年のTypeScriptでは、明示的に型を定義しない場合の暗黙の `any` を厳しく制限するトレンド(`noImplicitAny` など)が標準になっています。
結果として、「型が定まっていない危うい配列には、不用意にデータを追加させないでおこう」という挙動になるのです。
—
2. 【図解】型推論の流れと「情報の欠落」
ここで、TypeScriptがどうやって型を推論しているのか、イメージ図で見てみましょう。
[コードの記述]
const userIds = [];
│
▼ (中身が空で見えない!)
[型推論エンジン]
「中身の型がわからないぞ…」
│
▼ (安全のため、広すぎる型を仮置き)
let userIds: any[]
│
▼ (後から push(“user-001”) しようとする)
「本当にこの型で合ってる?」とコンパイラが警戒 ⚠️
つまり、「最初から空っぽの状態で変数を作ってしまうこと」が、情報不足(型迷子)を引き起こす原因なんです。
—
3. 解決策:正しく安全な配列の初期化パターン
では、この問題をクリアして、スッキリと配列に値を追加していくにはどうすればよいでしょうか? 実は、大きく分けて3つのアプローチがあります。
現場で一番よく使う順に見ていきましょう。
解決策A:最初から初期値を入れてあげる(型推論の魔法)
一番シンプルで、TypeScriptが一番喜ぶ書き方です。もし最初に入れるデータが少しでも分かっているなら、最初から詰めてしまいましょう。
// 最初から値を入れておくと、TypeScriptは「あ、文字列の配列なんだな」と完璧に推論します
const userIds = [“user-001”, “user-002”];
// これなら後から追加しても、ちゃんと型チェックが働きます!
userIds.push(“user-003”);
// 誤って数字を入れようとすると…?
// userIds.push(123); ❌ エラー: Argument of type ‘number’ is not assignable to parameter of type ‘string’.
TypeScriptに「最初から中身を教えてあげる」だけで、コンパイラは全自動で最高のサポートをしてくれます。
—
解決策B:型注釈(Type Annotation)を明示する
「いや、どうしても最初は絶対に空っぽの配列にしたいんだ!」という場合は、TypeScriptに対して「この配列は、将来こういうデータを入れる箱ですよ」と先回りして教えてあげます。これが型注釈です。
// 「この配列は string型 のみを入れる箱です」と明示する
const userIds: string[] = [];
// 最初は空でも、宣言通りの型であれば後から追加OK!
userIds.push(“user-001”);
userIds.push(“user-002”);
// 違う型を入れようとすると、しっかり怒ってくれます
// userIds.push(456); ❌ エラー!
書き方としては、変数名の後ろに `: 狙いたい型[]` を書くだけです。これですべてが解決します。
—
解決策C:ジェネリクス(`Array`)を使う
もう一つの表現として、TypeScriptのジェネリクス構文を使うこともできます。解決策Bの型注釈と意味は全く同じですが、コードの好みに応じて使い分けられます。
// Array
const scores: Array
scores.push(85);
scores.push(92);
// scores.push(“A”); ❌ エラー: numberの箱にstringは入れられません
フレームワークのコードやライブラリの型定義などでは、こちらの `Array
—
4. まとめ:今日から使えるマインドセット
空配列の `push` エラーに直面したときは、TypeScriptからのこんなメッセージだと受け取ってみてください。
> 「ねえ、君がこれからこの箱に何を入れたいのか、今のうちボクに教えてよ!」
- 解決のコツ1: 最初から値を入れて型推論に任せる
- 解決のコツ2: 空にするなら `const list: 期待する型[] = []` と先に宣言する
ここをスッとクリアできるようになると、TypeScriptの「型が守ってくれる安心感」が心地よくなってきますよ。
基本のキですが、実務でも毎日使う非常に大切なポイントです。ぜひ今日のコードから意識してみてくださいね!