TypeScript の配列とタプル
配列とタプルは、TypeScriptで最もよく使われる2つのコレクション型です。配列は「同じ型の値の集合」であり、タプルは「長さが固定されており、各要素の型が特定されている」配列の特殊な型です。
1. 配列の型
(1) 2つの注釈構文
TypeScript では、配列の型を記述する同等の方法が 2 つあります:
TYPESCRIPT
// Method 1:Type + square brackets(Recommendations,More intuitive)
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Charlie", "Diana", "Eric"];
// Method 2:Generic Arrays(Commonly used in generic scenarios)
let scores: Array<number> = [90, 85, 95];
let items: Array<string> = ["a", "b", "c"];
📌 推奨事項: 日常の開発では、より簡潔な
type[] という構文を使用してください。特定の複雑な型を扱う場合や、ジェネリック関数と併用する場合は、汎用構文 Array<T> の方が分かりやすくなります。
(2) 配列型の推論
TYPESCRIPT
let arr1 = [1, 2, 3]; // Inferred as number[]
let arr2 = ["a", "b", "c"]; // Inferred as string[]
let arr3 = [1, "hello", true]; // Inferred as (number | string | boolean)[]
let arr4 = []; // Inferred as any[](Danger!)
🔥 よくある間違い: 空の配列
[] は any[] と推論され、その後、異なる型の値を代入してもエラーにはなりません。厳格モードでは、明示的に let arr4: number[] = [] と指定することを推奨します。
(3) 多次元配列
TYPESCRIPT
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// Accessing Elements
console.log(matrix[0][1]); // 2
// Three-dimensional array
let cube: number[][][] = [[[1]]];
▶ 例:配列の基本操作
TYPESCRIPT
let fruits: string[] = ["Apple", "Banana", "Orange"];
// Add an element
fruits.push("Grapes"); // Add at the end
fruits.unshift("Strawberries"); // Add at the beginning
// Delete Element
let last = fruits.pop(); // Delete the end,Back "Grapes"
let first = fruits.shift(); // Delete the beginning,Back "Strawberries"
// Search
let index = fruits.indexOf("Banana"); // 0(The strawberries and grapes were removed.,The banana came in first)
// Iterate
fruits.forEach((fruit, i) => {
console.log(`${i}: ${fruit}`);
});
出力:
TEXT
0: Banana
1: Orange
2. 読み取り専用配列
読み取り専用配列は変更できません。要素の追加、削除、変更は許可されていません:
(1) readonly 修飾子
TYPESCRIPT
let readonlyArr: readonly number[] = [1, 2, 3];
// readonlyArr[0] = 10; // ❌ Read-only,Cannot be modified
// readonlyArr.push(4); // ❌ Read-only,Cannot be added
// readonlyArr.length = 0; // ❌ Read-only,The length cannot be changed
let normalArr: number[] = [1, 2, 3];
readonlyArr = normalArr; // ✅ A regular array can be assigned to a read-only array
// normalArr = readonlyArr; // ❌ A read-only array cannot be assigned to a regular array.
(2) ReadonlyArray ジェネリック
TYPESCRIPT
let readonlyArr2: ReadonlyArray<string> = ["a", "b", "c"];
// and readonly string[] Equivalent
💡 目的: 読み取り専用の配列を関数の引数として使用することで、呼び出し元に「この関数はあなたの配列を変更しません」ということを明確に伝えることができます:
TYPESCRIPT
function printItems(items: readonly string[]) {
items.forEach(item => console.log(item));
// items.push("new"); // ❌ Compilation Error——The function is guaranteed not to be modified
}
let myItems = ["a", "b"];
printItems(myItems); // Safety——myItems Will not be modified
3. タプル型
タプルは、「長さが固定されており、各要素の型が特定されている」配列であり、「名前付きの固定構造体」の軽量版と考えることができます。
(1) 基本的な構文
TYPESCRIPT
// Defining Tuples:[Type1, Type2, ...]
let user: [string, number] = ["Charlie", 20];
// Access by Index
console.log(user[0]); // "Charlie"(string Type)
console.log(user[1]); // 20(number Type)
// Assignments must match in type and length
// user = [20, "Charlie"]; // ❌ Type order does not match
// user = ["Charlie"]; // ❌ Not long enough
// user = ["Charlie", 20, true]; // ❌ Exceeds the length limit
(2) タプルと配列の違い
| フィーチャー | 配列 number[] |
タプル [string, number] |
|---|---|---|
| 長さ | 任意 | 固定 |
| 要素の種類 | すべて同じ | 位置によって異なる場合あり |
| アクセス | すべての要素が同じ型である | 各インデックスには特定の型がある |
| ユースケース | 類似のデータセット | 構造が固定された小規模なデータセット |
(3) タプルの展開
TYPESCRIPT
let pair: [string, number] = ["Apple", 5];
let [fruit, count] = pair;
console.log(fruit); // "Apple"
console.log(count); // 5
// Including remaining elements
let record: [string, number, ...boolean[]] = ["Test", 100, true, false, true];
let [name, score, ...flags] = record;
console.log(name); // "Test"
console.log(score); // 100
console.log(flags); // [true, false, true]
(4) 任意の要素
タプル内のオプション要素には、? を付けることができます:
TYPESCRIPT
let result: [string, number?] = ["Success"];
console.log(result[0]); // "Success"
console.log(result[1]); // undefined(Optional elements may be omitted.)
⚠️ Note: Optional elements can only appear at the end.
[string?, number] is invalid—no required elements can follow an optional element.
(5) タグ付きタプル
TypeScript 4.0 ではタプルラベルが導入され、各位置の意味がより明確になりました:
TYPESCRIPT
let user: [name: string, age: number] = ["Charlie", 20];
let point: [x: number, y: number, z?: number] = [1.0, 2.5];
// Tags are only for document purposes,Does not affect the type——But the code's readability has improved significantly
function getRange(): [min: number, max: number] {
return [0, 100];
}
let [min, max] = getRange();
console.log(`Scope:${min} - ${max}`); // Scope:0 - 100
▶ 例:関数の戻り値としてのタプル
TYPESCRIPT
// Tuples are ideal for representing the return values of functions."Multiple values"
function divide(a: number, b: number): [quotient: number, remainder: number] {
return [Math.floor(a / b), a % b];
}
let [quotient, remainder] = divide(17, 5);
console.log(`Quotient:${quotient},Remainder:${remainder}`); // Quotient:3,Remainder:2
// A tuple represents a key-value pair
function entries(obj: Record<string, number>): [string, number][] {
return Object.entries(obj) as [string, number][];
}
let scores = { Chinese Language: 90, Mathematics: 95, English: 88 };
for (let [subject, score] of entries(scores)) {
console.log(`${subject}:${score}pts`);
}
出力:
TEXT
Quotient:3,Remainder:2
Chinese Language:90pts
Mathematics:95pts
English:88pts
4. 配列に対する高階演算
(1) ユニオン型配列とユニオン型の配列
TYPESCRIPT
// Union Type Arrays:Each element in the array can be number or string
let mixed: (number | string)[] = [1, "two", 3, "four"];
// Union Types for Arrays:Either... or number[],Either... or string[]
let uniform: number[] | string[] = [1, 2, 3]; // ✅
uniform = ["a", "b", "c"]; // ✅
// uniform = [1, "two"]; // ❌ Do not mix
🔥 よくある間違い:
(number | string)[] と number[] | string[] はまったく異なる意味を持っています!前者は「混合配列」を指し、後者は「純粋な数値配列または純粋な文字列配列」を指します。
(2) 配列メソッドの型安全性
TYPESCRIPT
let nums: number[] = [3, 1, 4, 1, 5];
// sort An explicit comparison function is required,Otherwise, sort by string
nums.sort((a, b) => a - b); // ✅ Number Sorting:[1, 1, 3, 4, 5]
// map Automatic Inference of Return Types
let doubled = nums.map(n => n * 2); // Inferred as number[]
let asStrings = nums.map(String); // Inferred as string[]
// filter Automatically Infer Element Types
let big = nums.filter(n => n > 2); // Inferred as number[]
(3) 配列のアンパックと型
TYPESCRIPT
let a: number[] = [1, 2];
let b: number[] = [3, 4];
let merged: number[] = [...a, ...b]; // [1, 2, 3, 4]
// Tuple Unpacking
let prefix: [string, number] = ["No.", 0];
let full: [string, number, ...number[]] = [...prefix, 1, 2, 3];
// full = ["No.", 0, 1, 2, 3]
5. 実際の開発における選択肢
(1) 配列を使うべき場面
- 類似したデータの集まり(ユーザー一覧、スコア一覧、構成アイテム一覧など)
- 要素の数が不明
- トラバーサル、検索、ソートなどの操作が必要となる
(2) タプルを使うべき場合
- 長さと構造が固定された小規模なデータセット(座標
[x, y]、キー・値ペア[key, value]) - 関数は複数の値を返す
- 要素が2つか3つだけのシンプルな構造(より複雑な構造の場合は、オブジェクトやインターフェースを使用する)
(3) 読み取り専用配列を使用するタイミング
- 関数の引数 — 渡された配列を変更しないという約束
- 設定の固定化――誤った変更の防止
- APIの戻り値—データが不変であることをユーザーに通知する
▶ 例:総合演習—学生の成績管理
TYPESCRIPT
// Represented as a tuple [Subject, Fractions]
type Score = [subject: string, score: number];
// Storing Multiple Grades in an Array
let scores: Score[] = [
["Chinese Language", 92],
["Mathematics", 88],
["English", 95],
["Physics", 78],
["Chemistry", 85]
];
// Calculate the average score
function average(scores: readonly Score[]): number {
let total = scores.reduce((sum, [, score]) => sum + score, 0);
return total / scores.length;
}
// Find the subject with the highest score
function topSubject(scores: readonly Score[]): Score {
return scores.reduce((best, current) =>
current[1] > best[1] ? current : best
);
}
console.log("Average Score:" + average(scores).toFixed(1));
let [subject, score] = topSubject(scores);
console.log(`Subject with the Highest Score:${subject}(${score}pts)`);
出力:
TEXT
Average Score:87.6
Subject with the Highest Score:English(95pts)
❓ よくある質問
Q タプルと通常のオブジェクトの違いは何ですか?それぞれをいつ使用すべきですか?
A タプルはインデックス (
tuple[0]) によってアクセスされるのに対し、オブジェクトはキー (obj.name) によってアクセスされます。タプルは、2~3個の要素からなる単純で固定された構造(座標やキーと値のペアなど)に適しています。要素が3つ以上ある場合や、意味論が複雑な場合は、オブジェクトやインターフェースを使用してください。なぜなら、user.nameはuser[0]よりもはるかに読みやすいからです。Q (number | string)[] と number[] | string[] の違いは具体的に何ですか?
A
(number | string)[] は、「各要素が数値または文字列のいずれか」である配列です。つまり、[1, "a", 2] のように、両方の型が混在することが許されます。number[] | string[]は、「配列全体がすべて数値か、すべて文字列のいずれかである」という配列です。型が混在することは許可されていないため、[1, "a"]は無効です。後者は2つの配列型の和集合であり、ユニオン型の配列ではありません。Q タプルのpush/pop操作で型チェックが行われないのはなぜですか?
A これはTypeScriptにおける既知の設計上の欠陥です。タプル内での範囲外の要素へのアクセス(例:
tuple[100])はエラーを発生させますが、push/popメソッドは、厳格モードであっても型チェックされずに実行されてしまいます。変更を完全に防ぐためには、readonlyタプル(readonly [string, number])を使用することを推奨します。📖 まとめ
- 配列型の記述方法には、
type[](推奨)とArray<type>の2通りがありますが、これらはまったく同じ効果を持ちます。 readonly type[]およびReadonlyArray<type>は読み取り専用の配列を作成します。これらの配列の要素を追加、削除、または変更することはできません。- タプル
[type1, type2, ...]は、長さが固定された配列の一種であり、各要素は特定の型を持っています。 - タプルでは、デストラクチャリング、オプション要素 (
type?)、残りの要素 (...type[])、およびラベル ([name: string]) がサポートされています。 (A | B)[](ユニオン型の配列)とA[] | B[](ユニオン型の配列)は、まったく異なる意味を持つ
📝 練習問題
- 基本問題(難易度 ⭐):5つのプログラミング言語の名前を格納するための配列
string[]を作成し、forEachを使ってその要素を順に読み込み、出力してください。次に、読み取り専用バージョンreadonly string[]を作成し、それを変更してみて、表示されるエラーメッセージを確認してください。 - 上級問題(難易度 ⭐⭐):タプル内の2つの要素の位置と型を入れ替える関数
swap(tuple: [string, number]): [number, string]を定義してください。例えば、swap(["hello", 42])は[42, "hello"]を返します。 - チャレンジ問題(難易度 ⭐⭐⭐):CSVテキストファイルをタプルに解析する関数
parseCSV(lines: string[]): [header: string[], ...rows: string[][]]を実装してください。このファイルでは、1行目がヘッダー、残りの行がデータとなります。戻り値は、残りの要素を含むタグ付きタプルである必要があります。



