チュートリアル

TypeScript基礎:配列とコレクション

TypeScript入門配列コレクション
広告エリア

配列の型

基本的な配列

// 型[]記法
const numbers: number[] = [1, 2, 3];
const strings: string[] = ["a", "b", "c"];

// Array<型>記法
const booleans: Array<boolean> = [true, false];

// 型推論
const inferred = [1, 2, 3];  // number[]

// 混合配列(ユニオン型)
const mixed: (string | number)[] = [1, "two", 3];

多次元配列

// 2次元配列
const matrix: number[][] = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

// 3次元配列
const cube: number[][][] = [
  [[1, 2], [3, 4]],
  [[5, 6], [7, 8]]
];

読み取り専用配列

// readonly修飾子
const numbers: readonly number[] = [1, 2, 3];
// numbers.push(4);  // エラー

// ReadonlyArray型
const strings: ReadonlyArray<string> = ["a", "b", "c"];
// strings[0] = "x";  // エラー

// as const で完全な読み取り専用
const tuple = [1, 2, 3] as const;
// tuple は readonly [1, 2, 3] 型

タプル

固定長・固定型の配列です。

// 基本的なタプル
const user: [string, number] = ["太郎", 25];
const [name, age] = user;  // 分割代入

// ラベル付きタプル(可読性向上)
type User = [name: string, age: number];
const user2: User = ["花子", 30];

// オプション要素
type Point = [number, number, number?];
const point2D: Point = [10, 20];
const point3D: Point = [10, 20, 30];

// 残余要素
type StringNumberBooleans = [string, number, ...boolean[]];
const t: StringNumberBooleans = ["hello", 1, true, false, true];

読み取り専用タプル

type ReadonlyPoint = readonly [number, number];
const point: ReadonlyPoint = [10, 20];
// point[0] = 5;  // エラー

// as const でリテラル型
const colors = ["red", "green", "blue"] as const;
// colors は readonly ["red", "green", "blue"] 型

Map

// 基本的なMap
const userMap = new Map<number, string>();
userMap.set(1, "太郎");
userMap.set(2, "花子");

console.log(userMap.get(1));  // "太郎"
console.log(userMap.has(2));  // true
console.log(userMap.size);    // 2

// 初期値付き
const map = new Map<string, number>([
  ["one", 1],
  ["two", 2],
  ["three", 3]
]);

// イテレーション
for (const [key, value] of map) {
  console.log(`${key}: ${value}`);
}

// メソッド
map.delete("one");
map.clear();

オブジェクトをキーに

interface User {
  id: number;
  name: string;
}

const userScores = new Map<User, number>();

const user1: User = { id: 1, name: "太郎" };
const user2: User = { id: 2, name: "花子" };

userScores.set(user1, 100);
userScores.set(user2, 85);

console.log(userScores.get(user1));  // 100

Set

// 基本的なSet
const numberSet = new Set<number>();
numberSet.add(1);
numberSet.add(2);
numberSet.add(2);  // 重複は無視

console.log(numberSet.size);    // 2
console.log(numberSet.has(1));  // true

// 初期値付き
const stringSet = new Set<string>(["a", "b", "c"]);

// 配列から重複除去
const array = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(array)];  // [1, 2, 3]

// イテレーション
for (const value of stringSet) {
  console.log(value);
}

集合演算

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

// 和集合
const union = new Set([...setA, ...setB]);
// Set { 1, 2, 3, 4, 5, 6 }

// 積集合
const intersection = new Set([...setA].filter(x => setB.has(x)));
// Set { 3, 4 }

// 差集合
const difference = new Set([...setA].filter(x => !setB.has(x)));
// Set { 1, 2 }

WeakMap と WeakSet

キーへの参照が弱い(ガベージコレクション対象になる)コレクションです。

// WeakMap(キーはオブジェクトのみ)
const weakMap = new WeakMap<object, string>();
let obj = { id: 1 };
weakMap.set(obj, "data");
console.log(weakMap.get(obj));  // "data"

// obj = null にするとエントリもGC対象

// WeakSet
const weakSet = new WeakSet<object>();
weakSet.add({ id: 1 });

配列の型ガード

// 配列かどうかのチェック
function processValue(value: string | string[]): void {
  if (Array.isArray(value)) {
    // value は string[] 型
    value.forEach(item => console.log(item));
  } else {
    // value は string 型
    console.log(value);
  }
}

// null/undefinedを除去
const arr: (string | null)[] = ["a", null, "b", null, "c"];
const filtered: string[] = arr.filter((item): item is string => item !== null);

ユーティリティ型と配列

// 配列の要素型を取得
type ArrayElement<T> = T extends (infer U)[] ? U : never;

const numbers = [1, 2, 3];
type Num = ArrayElement<typeof numbers>;  // number

// タプルからユニオン型
const tuple = ["a", 1, true] as const;
type TupleUnion = (typeof tuple)[number];  // "a" | 1 | true

実践例:型安全なフィルタリング

interface User {
  id: number;
  name: string;
  role: "admin" | "user" | "guest";
  active: boolean;
}

const users: User[] = [
  { id: 1, name: "太郎", role: "admin", active: true },
  { id: 2, name: "花子", role: "user", active: false },
  { id: 3, name: "次郎", role: "user", active: true },
  { id: 4, name: "三郎", role: "guest", active: true }
];

// フィルタ条件の型
type FilterCondition<T> = {
  [K in keyof T]?: T[K] | T[K][];
};

// 型安全なフィルタ関数
function filterBy<T>(items: T[], condition: FilterCondition<T>): T[] {
  return items.filter(item => {
    for (const [key, value] of Object.entries(condition)) {
      const itemValue = item[key as keyof T];
      if (Array.isArray(value)) {
        if (!value.includes(itemValue as never)) return false;
      } else if (itemValue !== value) {
        return false;
      }
    }
    return true;
  });
}

// 使用例
const activeAdmins = filterBy(users, { role: "admin", active: true });
const activeUsers = filterBy(users, { role: ["admin", "user"], active: true });

実践例:グループ化

function groupBy<T, K extends keyof T>(
  items: T[],
  key: K
): Map<T[K], T[]> {
  const map = new Map<T[K], T[]>();

  for (const item of items) {
    const value = item[key];
    const group = map.get(value) || [];
    group.push(item);
    map.set(value, group);
  }

  return map;
}

// 使用例
interface Product {
  id: number;
  name: string;
  category: string;
}

const products: Product[] = [
  { id: 1, name: "りんご", category: "果物" },
  { id: 2, name: "にんじん", category: "野菜" },
  { id: 3, name: "バナナ", category: "果物" }
];

const grouped = groupBy(products, "category");
// Map { "果物" => [...], "野菜" => [...] }

まとめ

  • T[]またはArray<T>で配列の型を指定
  • readonlyで読み取り専用配列
  • タプルで固定長・固定型の配列
  • Map<K, V>でキーと値の型を指定
  • Set<T>で重複のないコレクション
  • Array.isArrayと型述語で型ガード
  • as constでリテラル型のタプル

次回はクラスについて学びます。

広告エリア