現代のフロントエンド開発やサーバーサイド開発において、TypeScriptは欠かせない存在となりました。
特にデータの集合を扱う場面では、配列(Array)と並んで「Set型」の重要性が非常に高まっています。
2026年現在、JavaScriptおよびTypeScriptの進化により、Set型は単なる「重複のないリスト」を超えた強力なツールへと変貌を遂げました。
本記事では、TypeScriptにおけるSet型の基本的な使い方から、Genericsを用いた型安全な実装、さらには近年標準化された高度な集合操作メソッドまでを網羅的に解説します。
TypeScriptにおけるSet型の基本概念
Set型は、JavaScriptの標準組み込みオブジェクトである「Set」をTypeScriptで型安全に扱うための仕組みです。
Setの最大の特徴は、保持する要素がすべて一意(ユニーク)であることです。
同じ値を二度追加しようとしても、Set内には一つのインスタンスしか保持されません。
これは、IDの一覧やフラグの管理、あるいは重複を許容しないデータのストックにおいて非常に強力な特性を発揮します。
TypeScriptでは、このSetに対してGenerics(ジェネリクス)を用いることで、格納する要素の型を厳密に制限できます。
例えば、文字列のみを格納するSetを定義する場合、以下のように記述します。
// 文字列のみを許可するSetの定義
const userRoles = new Set<string>();
userRoles.add("admin");
userRoles.add("editor");
userRoles.add("admin"); // 重複しているため無視される
console.log(userRoles.size);
2
このように、TypeScriptの型システムと組み合わせることで、実行時のエラーを防ぎつつ、直感的なデータ操作が可能になります。
Set型の主なメソッドと基本的な操作方法
Set型を使いこなすためには、まず基本となるメソッドを正しく理解する必要があります。
配列とは異なり、Setは要素の順序に依存したアクセス(インデックスによるアクセス)が推奨されない代わりに、特定要素の存在確認が高速(O(1))であるというメリットがあります。
要素の追加と削除
要素を追加するには add() メソッドを使用し、削除するには delete() メソッドを使用します。
add() メソッドはSetオブジェクト自身を返すため、メソッドチェーンを利用して連続的に追加することも可能です。
const tags = new Set<string>();
// メソッドチェーンによる追加
tags.add("TypeScript").add("JavaScript").add("Web");
// 要素の削除
tags.delete("JavaScript");
console.log(tags);
Set(2) {"TypeScript", "Web"}
存在確認と全クリア
ある値がSetに含まれているかを確認するには has() メソッドを使用します。
これは配列の includes() メソッドに相当しますが、データ量が増えた場合でも高速に動作します。
また、すべての要素を一括で削除したい場合は clear() メソッドを使用します。
const permissions = new Set<string>(["read", "write"]);
if (permissions.has("read")) {
console.log("読み取り権限があります");
}
permissions.clear();
console.log(permissions.size);
読み取り権限があります
0
TypeScriptでの型安全なSetの活用
TypeScriptでSetを扱う際、最も重要になるのがGenericsの指定です。
型を指定せずに new Set() を実行すると、初期値から型推論が行われますが、空の状態で初期化する場合は明示的な型指定が推奨されます。
Union型との組み合わせ
Setにはリテラル型やUnion型を指定することもできます。
これにより、特定の限定された文字列や数値のみを許容するSetを構築できます。
type Status = "active" | "inactive" | "pending";
const activeStatuses = new Set<Status>(["active", "pending"]);
// エラー:Status型に含まれない値は追加できない
// activeStatuses.add("deleted");
このように、ドメイン駆動設計やステート管理において、不適切な状態が混入するのを防ぐことができます。
ReadonlySetによるイミュータビリティの確保
関数の引数としてSetを渡す際、その関数内でSetの内容が書き換えられるのを防ぎたい場合があります。
その場合は ReadonlySet<T> 型を使用します。
ReadonlySet には add() や delete() といった破壊的なメソッドが存在しないため、安全にデータを参照専用として扱うことができます。
function checkAccess(roles: ReadonlySet<string>, target: string): boolean {
// roles.add("guest"); // コンパイルエラーが発生する
return roles.has(target);
}
大規模なアプリケーション開発において、データの不整合を防ぐためにReadonlySetの活用は非常に効果的です。
待望の標準集合操作メソッド
2024年から2025年にかけて、JavaScriptの Set オブジェクトには待望の新しいメソッドが追加されました。
2026年現在では、主要なブラウザやNode.js環境でこれらのメソッドをTypeScriptからネイティブに利用することが一般的となっています。
これまではLodashなどの外部ライブラリを使用したり、配列に変換してフィルタリングしたりする必要があった操作が、メソッド一つで完結します。
和集合 (union)
union() メソッドは、二つのSetに含まれるすべての要素を合わせた新しいSetを返します。
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
const combined = setA.union(setB);
console.log(Array.from(combined));
[1, 2, 3, 4, 5]
積集合 (intersection)
intersection() メソッドは、両方のSetに共通して存在する要素のみを抽出した新しいSetを返します。
const setA = new Set([1, 2, 3]);
const setB = new Set([2, 3, 4]);
const common = setA.intersection(setB);
console.log(Array.from(common));
[2, 3]
差集合 (difference)
difference() メソッドは、元のSetから、別のSetに含まれる要素を取り除いた新しいSetを返します。
const allUsers = new Set(["Alice", "Bob", "Charlie"]);
const activeUsers = new Set(["Bob"]);
const inactiveUsers = allUsers.difference(activeUsers);
console.log(Array.from(inactiveUsers));
["Alice", "Charlie"]
対称差 (symmetricDifference)
symmetricDifference() メソッドは、どちらか一方のSetにのみ存在する要素をすべて集めた新しいSetを返します。
const setA = new Set([1, 2, 3]);
const setB = new Set([3, 4, 5]);
const diff = setA.symmetricDifference(setB);
console.log(Array.from(diff));
[1, 2, 4, 5]
包含関係の判定 (isSubsetOf / isSupersetOf / isDisjointFrom)
要素の包含関係を真偽値(boolean)で判定するメソッドも追加されています。
| メソッド名 | 説明 |
|---|---|
isSubsetOf(other) | そのSetが other の部分集合であるかを判定します。 |
isSupersetOf(other) | そのSetが other を完全に含んでいる(上位集合である)かを判定します。 |
isDisjointFrom(other) | 二つのSetに共通の要素が一つも存在しないかを判定します。 |
これらのメソッドにより、数学的な集合操作を宣言的かつ簡潔に記述できるようになりました。
ArrayとSetの使い分けとパフォーマンスの比較
TypeScript開発において「配列(Array)を使うべきか、Setを使うべきか」は頻繁に議論されるテーマです。
適切な選択を行うためには、それぞれのデータ構造の特性を理解する必要があります。
計算量の違い
配列で特定の要素が含まれているかを確認する場合(includes)、計算量は要素数に比例する O(n) です。
一方、Setの has メソッドはハッシュテーブルアルゴリズムを使用しているため、計算量は O(1)、つまり定数時間で済みます。
数千から数万件のデータから特定の値を検索する場合、Setの利用は劇的なパフォーマンス向上をもたらします。
使い分けの基準
基本的には、以下の基準で判断すると良いでしょう。
- 配列(Array)を使うべき場面:データの順序が重要である場合、要素の重複を許可したい場合、インデックスを指定してアクセスしたい場合。
- Setを使うべき場面:要素のユニーク性を保証したい場合、高速な検索(存在確認)が必要な場合、数学的な集合操作(積集合など)を行いたい場合。
特に、「IDリスト」のような一意性が求められるIDの集まりを扱う際は、迷わずSetを選択することをおすすめします。
Setを扱う際の実践的なテクニック
Set型は単独で使うだけでなく、他の構文や機能と組み合わせることで真価を発揮します。
配列からの重複除去(デデュープ)
最も有名な活用例は、配列から重複する要素を取り除く操作です。
TypeScriptではスプレッド構文を用いることで、簡潔に記述できます。
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers);
[1, 2, 3, 4, 5]
オブジェクトをSetに格納する際の注意点
Setはプリミティブ値(string, number, booleanなど)の比較には厳密等価演算子を使用しますが、オブジェクト(Object, Array)の比較は参照に基づいて行われます。
内容が全く同じオブジェクトであっても、メモリ上の参照先が異なれば別々の要素として扱われる点に注意してください。
const objSet = new Set<{id: number}>();
const item = { id: 1 };
objSet.add(item);
objSet.add({ id: 1 }); // 別の参照なので追加されてしまう
console.log(objSet.size);
2
オブジェクトの一意性を保証したい場合は、オブジェクト自体をSetに入れるのではなく、一意なIDなどを抽出してSetで管理する設計を検討しましょう。
Setから配列への変換
Setには map() や filter() といった配列でおなじみの高階関数が直接存在しません(2026年時点)。
そのため、これらの操作を行いたい場合は一旦配列に変換する必要があります。
const mySet = new Set([1, 2, 3]);
// 配列に変換して操作
const doubledArray = Array.from(mySet).map(n => n * 2);
console.log(doubledArray);
Array.from() を使う方法は、スプレッド構文 [...mySet] よりも可読性が高く、意図が明確になる場合があります。
WeakSetとの違い
Set型と似た型に WeakSet があります。
名前の通り「弱い参照」を保持するSetですが、通常のSetとは決定的な違いがいくつかあります。
- 格納できるのはオブジェクトのみである(プリミティブ値は不可)。
- 反復(Iteration)が不可能である(
forEachやsizeプロパティが存在しない)。 - ガベージコレクション(GC)の対象となる。
WeakSetは、特定のオブジェクトに対してメタ情報を紐付けたいが、そのオブジェクトがメモリから解放されるのを妨げたくない、といったメモリリーク対策が必要な高度な用途で使用されます。
日常的なアプリケーション開発では、まず通常の Set を検討し、特殊なメモリ管理が必要な場合のみ WeakSet を選ぶのが一般的です。
まとめ
TypeScriptにおけるSet型は、データのユニーク性を保証し、高速な検索を可能にする極めて強力なデータ構造です。
特にGenericsを活用した型定義や、2026年現在で標準となった union() や intersection() といった集合操作メソッドの登場により、その利便性は飛躍的に向上しました。
「とりあえず配列(Array)」と考えるのではなく、扱うデータの性質や操作の内容に応じてSetを選択することで、コードの可読性とパフォーマンスの両面で大きなメリットを享受できます。
本記事で紹介したテクニックを参考に、ぜひ日々の開発で型安全なSetの活用を推進してください。
堅牢な型システムと洗練された集合操作を組み合わせることで、より品質の高いTypeScriptプログラムが実現できるはずです。
