💡
TypeScriptのサンプルコード
文法解説で学んだ知識を組み合わせた、難易度が段階的に上がる3本の実践的なサンプルプログラムです。①型定義を使った小規模アプリ、②ジェネリクスを使ったデータ構造とアルゴリズム、③型ガード・判別可能なUnion型を使った高度な型の活用、の順に解説します。
サンプル1:型定義を使ったタスク管理アプリ
interface・リテラル型のユニオン・readonly/オプショナルプロパティを組み合わせた、シンプルなタスク管理プログラムです。
優先度(priority)はリテラル型のユニオンで表現し、許可されていない値は代入できません。
classにタスクの追加・完了・優先度順の一覧表示をまとめています。
SAMPLE PROGRAM
type Priority = "low" | "medium" | "high";
interface Task {
readonly id: number; // 一度発行したら変更しない
title: string;
priority: Priority;
done: boolean;
dueDate?: string; // 期限は省略可能
}
class TaskManager {
private tasks: Task[] = [];
private nextId = 1;
add(title: string, priority: Priority, dueDate?: string): Task {
const task: Task = {
id: this.nextId++,
title,
priority,
done: false,
dueDate,
};
this.tasks.push(task);
return task;
}
complete(id: number): boolean {
const target = this.tasks.find((t) => t.id === id);
if (!target) return false;
target.done = true;
return true;
}
// 優先度順(high > medium > low)に並べた未完了タスク一覧
listPending(): Task[] {
const order: Record<Priority, number> = { high: 0, medium: 1, low: 2 };
return this.tasks
.filter((t) => !t.done)
.sort((a, b) => order[a.priority] - order[b.priority]);
}
}
const manager = new TaskManager();
manager.add("設計書レビュー", "high", "2026-07-10");
manager.add("備品発注", "low");
manager.add("テストコード作成", "medium");
manager.complete(2);
for (const task of manager.listPending()) {
console.log(`[${task.priority}] ${task.title}${task.dueDate ? " (期限: " + task.dueDate + ")" : ""}`);
}
// [high] 設計書レビュー (期限: 2026-07-10)
// [medium] テストコード作成
サンプル2:ジェネリックなキューを使ったグラフの幅優先探索(BFS)
ジェネリクスで実装した汎用的なキュー(FIFO:先入れ先出し)を使い、グラフの最短経路をたどる幅優先探索(BFS)を実装します。 キューへのenqueue/dequeueはいずれも計算量 O(1)、BFS全体の計算量は頂点数V・辺数Eに対して O(V + E) です。
SAMPLE PROGRAM
class Queue<T> {
private items: T[] = [];
enqueue(item: T): void { // O(1)
this.items.push(item);
}
dequeue(): T | undefined { // O(1)(配列先頭のshiftはO(n)になるため、内部的にはindex管理でも良い)
return this.items.shift();
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
// 隣接リストで表現したグラフ
type Graph = Map<string, string[]>;
function bfs(graph: Graph, start: string, goal: string): string[] | null {
const queue = new Queue<string[]>(); // 経路(パス)をキューに積む
const visited = new Set<string>([start]);
queue.enqueue([start]);
while (!queue.isEmpty()) {
const path = queue.dequeue();
if (!path) continue;
const node = path[path.length - 1];
if (node === goal) return path; // 目的地に到達
const neighbors = graph.get(node) ?? [];
for (const next of neighbors) {
if (!visited.has(next)) {
visited.add(next);
queue.enqueue([...path, next]);
}
}
}
return null; // 到達不可能
}
const graph: Graph = new Map([
["A", ["B", "C"]],
["B", ["D"]],
["C", ["D"]],
["D", ["E"]],
]);
console.log(bfs(graph, "A", "E")); // ["A", "B", "D", "E"] または ["A", "C", "D", "E"]
サンプル3:判別可能なUnion型と型ガードを使った図形の面積計算
「判別可能なユニオン型(Discriminated Union)」は、共通のリテラル型プロパティ(ここではkind)で種類を判別するパターンです。
switch文のdefault節で戻り値をnever型の変数に代入することで、将来図形の種類を追加した際に分岐漏れがあればコンパイルエラーで検知できる「網羅性チェック」を行っています。
SAMPLE PROGRAM
interface Circle {
kind: "circle";
radius: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
type Shape = Circle | Rectangle | Triangle;
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2; // ここではCircleに絞り込まれている
case "rectangle":
return shape.width * shape.height; // ここではRectangleに絞り込まれている
case "triangle":
return (shape.base * shape.height) / 2; // ここではTriangleに絞り込まれている
default: {
// 全パターンを処理済みならshapeはnever型になる(網羅性チェック)
const exhaustiveCheck: never = shape;
throw new Error(`未対応の図形です: ${exhaustiveCheck}`);
}
}
}
const shapes: Shape[] = [
{ kind: "circle", radius: 2 },
{ kind: "rectangle", width: 3, height: 4 },
{ kind: "triangle", base: 5, height: 6 },
];
for (const s of shapes) {
console.log(`${s.kind}: ${area(s).toFixed(2)}`);
}
// circle: 12.57
// rectangle: 12.00
// triangle: 15.00