TypeScript | 基礎文法:配列・タプル – 配列mapの型推論

TypeScript
スポンサーリンク

「配列mapの型推論」とは何をしてくれているのか

map は、「配列の各要素を変換して、新しい配列を作る」メソッドです。
TypeScriptは、このときに「元の配列の要素の型」と「変換関数の戻り値の型」から、新しい配列の型を自動で推論してくれます。

const nums = [1, 2, 3];          // number[]
const doubled = nums.map(n => n * 2);
// doubled の型: number[]
TypeScript

ここで TypeScript は、

  • nums の要素は number
  • map のコールバックは number を受け取って number を返している

と理解して、doublednumber[] と推論します。
「元の配列の要素型」と「コールバックの戻り値の型」——この2つが、map の型推論の主役です。


単純な number[] / string[] の map からイメージを掴む

number[] を number[] に変換する

const scores = [80, 90, 100]; // number[]

const plusTen = scores.map(score => score + 10);
// plusTen: number[]
TypeScript

ここでの流れはこうです。

  • scores の要素型は number
  • map のコールバック score => score + 10 は、score: number を受け取って number を返している
  • だから、map の結果は number[]

コールバックの引数 score に型を書いていなくても、
「元の配列が number[] だから、ここは number だな」と TypeScript が推論してくれます。

scores.map(score => {
  // score: number と推論されている
  return score.toFixed(2); // ここで string を返す
});
// 戻り値の配列の型は string[]
TypeScript

「引数の型も」「戻り値の配列の型も」両方推論されているのがポイントです。

string[] を string[] に変換する

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

const upper = names.map(name => name.toUpperCase());
// upper: string[]
TypeScript

ここでも同じです。

  • names の要素型は string
  • コールバックは string を受け取って string を返している
  • 結果は string[]

「元の配列の要素型 → コールバックの引数の型 → 戻り値の配列の型」という流れが、きれいに繋がっています。


map で「型を変える」場合の推論

number[] → string[] に変換する

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

const labels = nums.map(n => `No.${n}`);
// labels: string[]
TypeScript

ここでは、

  • 元の配列の要素型: number
  • コールバックの戻り値の型: string
  • 結果の配列の型: string[]

というふうに、「戻り値の型」がそのまま「新しい配列の要素型」になります。

オブジェクトの配列に変換する

const ids = [1, 2, 3]; // number[]

const users = ids.map(id => ({ id, name: `user-${id}` }));
// users: { id: number; name: string }[]
TypeScript

ここでは、コールバックが { id: number; name: string } というオブジェクトを返しているので、
結果の配列は「そのオブジェクトの配列」として推論されます。

const firstUser = users[0];
// firstUser: { id: number; name: string }
TypeScript

map の中で「どんな形の値を返しているか」が、そのまま配列の型になります。
「配列の map は、“要素の型を変換する機械”」というイメージを持つと分かりやすいです。


union型配列に対する map の型推論

(number | string)[] を map する

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

const asString = mixed.map(v => v.toString());
// asString: string[]
TypeScript

ここでは、

  • 元の要素型: number | string
  • コールバックの引数 v の型も number | string
  • v.toString() の戻り値は string
  • 結果の配列は string[]

となります。

条件分岐で型を絞り込む場合

const lengths = mixed.map(v => {
  if (typeof v === "number") {
    return v * 2;        // number
  } else {
    return v.length;     // number(string の length)
  }
});
// lengths: number[]
TypeScript

ここでは、if の中で typeof を使って型を絞り込んでいますが、
どちらの分岐でも number を返しているので、結果の配列は number[] になります。

もし、分岐ごとに違う型を返したらどうなるか。

const mixedResult = mixed.map(v => {
  if (typeof v === "number") {
    return v * 2;        // number
  } else {
    return v.toUpperCase(); // string
  }
});
// mixedResult: (number | string)[]
TypeScript

この場合、戻り値の型が number | string になるので、
結果の配列も (number | string)[] と推論されます。


map のコールバック引数の型推論(わざわざ書かなくていいところ)

引数の型は「元の配列の要素型」から自動で決まる

const nums = [1, 2, 3];

nums.map(n => {
  // n: number と推論されている
  return n * 2;
});
TypeScript

ここで n: number とわざわざ書かなくても、
TypeScript は「numsnumber[] だから、map のコールバックの引数も number だな」と推論してくれます。

同じく、インデックスや配列全体も推論されます。

nums.map((value, index, array) => {
  // value: number
  // index: number
  // array: number[]
  return value + index;
});
TypeScript

map のコールバックは (value, index, array) の3つを受け取れますが、
それぞれの型も「元の配列の型」から自動で決まります。


map と any[] / unknown[] の危険な組み合わせ

any[] だと、map の中も型チェックが効かない

const xs: any[] = [1, "2", true];

const result = xs.map(x => x.toFixed(2)); // これも通ってしまう
TypeScript

any は「何でもアリ」なので、x.toFixed が存在するかどうかもチェックされません。
実行時に x が string や boolean だったら落ちますが、コンパイル時には分かりません。

map の型推論をちゃんと効かせたいなら、
元の配列を any[] にしないことがとても重要です。

unknown[] の場合

const xs: unknown[] = [1, "2", true];

xs.map(x => {
  // x: unknown
  // x.toFixed(2); // エラー:unknown にはメソッドがない
});
TypeScript

unknown は「何か分からない」なので、そのままでは何もできません。
使う前に型ガードで絞り込む必要があります。

xs.map(x => {
  if (typeof x === "number") {
    return x.toFixed(2); // ここでは number として扱える
  }
  return String(x);
});
// 戻り値の配列の型: string[]
TypeScript

unknown[] の方が any[] より安全ですが、
「ちゃんと絞り込んでから使う」という一手間が必要になります。


初心者がまず押さえておきたい「配列mapの型推論」の感覚

配列の map における型推論の本質は、たった2つです。

  • 「元の配列の要素型」から、コールバックの引数の型が決まる
  • 「コールバックの戻り値の型」から、新しい配列の要素型が決まる

この2つさえ掴んでおけば、

  • number[]string[] に変換する
  • number[]{ id: number }[] に変換する
  • (number | string)[]string[] に変換する

といったパターンが、自然に読めるようになります。

そしてもうひとつ大事なのは、
「元の配列の型がきちんと決まっているほど、map の型推論は気持ちよく働く」
ということです。

any[] に逃げない
unknown[] を使うなら、ちゃんと絞り込む
ユニオン型配列なら、「何が混ざるのか」を意識して設計する

こういう意識を持って map を使っていくと、
「配列を変換するたびに、型が自然についてくる」感覚が、かなりはっきり見えてきます。

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