TypeScript | 基礎文法:配列・タプル – 配列操作時の型エラー対処

TypeScript
スポンサーリンク

よくある「配列操作の型エラー」はどこから来るのか

TypeScriptで配列を触り始めると、かなりの確率で型エラーにぶつかります。
でも、その多くは「TypeScriptが何を守ろうとしているか」が分かると、パターンとして整理できます。

ざっくり言うと、配列まわりの型エラーは次のようなところから生まれます。

  • 要素の型が合っていない(string[]number を入れようとしている など)
  • readonly な配列を「書き換えよう」としている
  • undefinednull の可能性を無視している
  • ユニオン型配列を「単一の型」として扱っている

ここから、典型的なエラーと、その「読み方」「直し方」を具体的に見ていきます。


パターン1:要素の型が合わないときのエラー

症状とエラーメッセージの例

const names: string[] = ["Taro", "Hanako"];

names.push(123);
// Type 'number' is not assignable to type 'string'.
TypeScript

TypeScriptは「namesstring[] だから、要素は全部 string であるべき」と理解しています。
そこに number を入れようとすると、「その型はこの配列には入れられない」と怒られます。

対処の考え方

ここでやるべきことは、「どっちを直すべきか」を冷静に決めることです。

  • 本当に文字列だけを入れたい配列なら、123String(123)"123" に変える
  • 「文字列と数値が混ざる配列」にしたいなら、型を (string | number)[] に変える
const mixed: (string | number)[] = ["Taro", 123];
mixed.push("Hanako");
mixed.push(456);
TypeScript

「配列の型」と「実際に入れたい値」がズレているからエラーになっているので、
どちらを正とするかを決めて、片方を合わせにいく——これが基本の直し方です。


パターン2:readonly配列にpushしようとして怒られる

症状とエラーメッセージの例

const nums: readonly number[] = [1, 2, 3];

nums.push(4);
// Property 'push' does not exist on type 'readonly number[]'.  [TypeScript入門『サバイバルTypeScript』](https://typescriptbook.jp/reference/values-types-variables/array/readonly-array)  [IT trip](https://ittrip.xyz/typescript/typescript-readonly-array-usage)
TypeScript

readonly number[] は「読み取り専用の配列」です。
TypeScriptは「この配列は変更してはいけない」と理解しているので、
pushpop のような「中身を変えるメソッド」はそもそも存在しないことになっています。

対処の考え方

ここでやってはいけないのは、「とりあえず as number[] でキャストして無理やり push する」ことです。
それをやると、「本来変えちゃいけない配列を、型をごまかして書き換える」ことになり、バグの温床になります。

素直な対処は、「新しい配列を作る」ことです。

const nums: readonly number[] = [1, 2, 3];

const extended = [...nums, 4]; // OK
// extended: number[]
TypeScript

readonly な配列は「壊さない」。
どうしても要素を足したいなら、「コピーして新しい配列を作る」という発想に切り替えるのが、TypeScript的に正しい姿です。


パターン3:undefined / null を無視して怒られる

症状とエラーメッセージの例

const names: (string | undefined)[] = ["Taro", undefined, "Hanako"];

const first = names[1];
// first: string | undefined

first.toUpperCase();
// Object is possibly 'undefined'.  [Qiita](https://qiita.com/mukai3/items/6cc6c98c0a2f80274599)
TypeScript

TypeScriptは「names[1] は string かもしれないし、undefined かもしれない」と見ています。
そのまま toUpperCase を呼ぶと、「undefined だったら落ちるよね?」と止めてくれるわけです。

対処の考え方

ここでやるべきことは、「undefined の可能性をちゃんと処理する」ことです。

if (first !== undefined) {
  // ここでは first: string に絞り込まれる
  console.log(first.toUpperCase());
}
TypeScript

あるいは、「undefined のときは代わりの値を使う」という書き方もよく使います。

const safe = (first ?? "").toUpperCase();
TypeScript

配列操作で findfilter を使ったときも同じで、
「戻り値に undefined が含まれているなら、それをどう扱うかをコードで表現する」
これが型エラーを消す、というより「ちゃんと設計する」ための一歩になります。


パターン4:ユニオン型配列を「単一の型」として扱ってしまう

症状の例

const mixed: (number | string)[] = [1, "2", 3];

mixed.forEach(v => {
  v.toFixed(2);
  // Property 'toFixed' does not exist on type 'string | number'.
});
TypeScript

v の型は number | string なので、
TypeScriptは「string の可能性もあるから、number 専用メソッドは呼べない」と判断します。

対処の考え方

ここで必要なのは、「型を絞り込む」ことです。

mixed.forEach(v => {
  if (typeof v === "number") {
    // ここでは v: number
    console.log(v.toFixed(2));
  } else {
    // ここでは v: string
    console.log(v.toUpperCase());
  }
});
TypeScript

「ユニオン型配列」は、「混ざっていること」を型が教えてくれている状態です。
それを「どの場面でどの型に絞るか」を、if や型述語で丁寧に書いていく——
これが、配列操作時の型エラーを“正しく”解消する道筋になります。


パターン5:空配列・空オブジェクトを初期値にして型が変になる

症状の例(reduceなどでよく出る)

const nums = [1, 2, 3];

const result = nums.reduce((acc, n) => {
  acc.push(n * 2);
  return acc;
}, []);
// エラーになったり、acc が never[] になったりしがち
TypeScript

[] の型はデフォルトだと never[] です。
「何も入っていないから、要素型が推論できない → 何も入れられない配列」という扱いになります。

対処の考え方

ここは「型を明示する」のが一番きれいです。

const result = nums.reduce<number[]>((acc, n) => {
  acc.push(n * 2);
  return acc;
}, []);
// result: number[]
TypeScript

あるいは、初期値側に型注釈を書く方法もあります。

const result: number[] = nums.reduce((acc, n) => {
  acc.push(n * 2);
  return acc;
}, []);
TypeScript

「空の値」を初期値にするときは、
「これは何の配列(何のオブジェクト)なのか」を型で教えてあげる
これだけで、かなりの型エラーが消えていきます。


パターン6:代入先の配列型と右辺の配列型が合わない

症状の例

const nums: number[] = [1, 2, 3];

const xs: string[] = nums;
// Type 'number[]' is not assignable to type 'string[]'.
TypeScript

TypeScriptは「string[] には string しか入らない」と見ているので、
number[] をそのまま代入することはできません。

対処の考え方

ここも、「どっちが正しい仕様か」を決める必要があります。

  • 本当に文字列の配列が欲しいなら、変換する:
const xs: string[] = nums.map(n => n.toString());
TypeScript
  • 「数値の配列でよかった」と思うなら、左側の型を number[] に変える:
const xs: number[] = nums;
TypeScript

「型エラーを消す」のではなく、「仕様をはっきりさせる」
その結果として型エラーが消える、という順番で考えると、設計の筋が良くなります。


まとめ:配列の型エラーは「TypeScriptが何を守ろうとしているか」を読む

配列操作時の型エラーは、最初はうるさく感じるかもしれません。
でも、その裏側には必ず「TypeScriptが守ろうとしている前提」があります。

  • この配列には、この型の要素しか入らないはず
  • この配列は readonly だから、壊されては困る
  • ここには undefined が混ざる可能性がある
  • この配列は複数の型が混ざっているから、どの型かをちゃんと見てほしい

エラーメッセージを「邪魔者」としてではなく、
「仕様のズレを教えてくれるレビューコメント」として読む癖がつくと、
配列まわりのコードは一気に安定していきます。

タイトルとURLをコピーしました