TypeScriptとは何か?
TypeScriptは、MicrosoftがオープンソースとしてリリースしたJavaScriptのスーパーセット(上位互換言語)です。通常のJavaScriptコードはそのままTypeScriptとして動作しますが、TypeScriptでは「静的型付け」という強力な機能が追加されています。
JavaScriptは動的型付け言語で、変数の型は実行時に決まります。これは柔軟ですが、大規模プロジェクトでは「型の不一致」によるバグが発生しやすいという欠点があります。TypeScriptはコンパイル時に型エラーを検出し、バグを事前に防ぎます。
TypeScriptのメリット
- 型安全性:型エラーをコンパイル時に検出できる
- IDE支援:VS Codeなどで強力な補完・リファクタリング機能が使える
- 可読性向上:コードの意図が型定義によって明確になる
- 大規模開発に適している:チーム開発でのミスを減らせる
- 最新JS機能:TypeScriptコンパイラが古いJSへのダウンコンパイルを担当
TypeScriptのインストールと基本設定
まずNode.jsとnpmが必要です。インストール後、以下のコマンドでTypeScriptをインストールします:
# グローバルインストール
npm install -g typescript # バージョン確認
tsc --version
プロジェクトの初期化は tsconfig.json で行います:
# tsconfig.json を生成
tsc --init
生成された tsconfig.json の主要な設定項目:
{ "compilerOptions": { "target": "ES2020", // コンパイル後のJSバージョン "module": "commonjs", // モジュール形式 "strict": true, // 厳格モード(推奨) "outDir": "./dist", // 出力先ディレクトリ "rootDir": "./src" // ソースファイルの場所 }
}
基本的な型の使い方
TypeScriptの型は変数宣言の後に : 型名 のように記述します(型アノテーション)。ただし、TypeScriptは多くの場合、型を自動的に推論できます。
プリミティブ型
// string型
let name: string = "田中太郎";
let greeting = "こんにちは"; // 型推論でstring型 // number型
let age: number = 25;
let price: number = 3.14; // boolean型
let isActive: boolean = true; // null / undefined
let empty: null = null;
let notSet: undefined = undefined; // any型(型チェックを無効化 - 非推奨)
let anything: any = "文字列でも数値でもOK";
anything = 42; // エラーにならない 配列型
// 配列の型指定(2通りの書き方)
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: Array<string> = ["apple", "banana", "cherry"]; // タプル型(要素数と各型が固定)
let person: [string, number] = ["田中", 25];
// person[0] は string、person[1] は number オブジェクト型
// インラインでオブジェクト型を定義
let user: { name: string; age: number; email?: string } = { name: "山田花子", age: 30 // email は ? がついているのでオプション(省略可能)
}; // type エイリアスを使う方法
type User = { name: string; age: number; email?: string;
}; const newUser: User = { name: "鈴木", age: 28 }; ? をつけると「オプショナルプロパティ」になります。省略可能なフィールドに使います。インターフェース(Interface)
インターフェースはオブジェクトの「形(shape)」を定義するための仕組みです。type エイリアスと似ていますが、拡張(extends)や実装(implements)に向いています。
// インターフェース定義
interface Animal { name: string; age: number; speak(): string; // メソッドシグネチャ
} // インターフェースの実装
const dog: Animal = { name: "ポチ", age: 3, speak() { return "ワン!"; }
}; // インターフェースの拡張
interface Pet extends Animal { owner: string;
} const cat: Pet = { name: "タマ", age: 2, speak() { return "ニャー!"; }, owner: "田中"
}; クラスへの実装
interface Drawable { draw(): void; color: string;
} class Circle implements Drawable { color: string; radius: number; constructor(color: string, radius: number) { this.color = color; this.radius = radius; } draw(): void { console.log(`${this.color}の円を描画(半径:${this.radius}`); } area(): number { return Math.PI * this.radius ** 2; }
} const circle = new Circle("赤", 5);
circle.draw(); // "赤の円を描画(半径:5)" type vs interface:ほとんどの場合はどちらでも使えますが、クラスのimplementsや将来的に拡張する場合は interface を、ユニオン型など複雑な型合成には type を使うのが一般的です。関数の型定義
TypeScriptでは関数の引数と戻り値に型を指定できます。これにより、間違った使い方をすぐに検出できます。
// 引数と戻り値の型を指定
function add(a: number, b: number): number { return a + b;
} // アロー関数
const multiply = (x: number, y: number): number => x * y; // オプショナルパラメータ
function greet(name: string, prefix?: string): string { return `${prefix ?? "こんにちは"}, ${name}!`;
} greet("田中"); // "こんにちは, 田中!"
greet("田中", "おはよう"); // "おはよう, 田中!" // デフォルト値
function createUser(name: string, role: string = "user") { return { name, role };
} // 戻り値なし(void型)
function logMessage(msg: string): void { console.log(msg);
} ユニオン型とインターセクション型
// ユニオン型(AまたはB)
type StringOrNumber = string | number; function formatId(id: StringOrNumber): string { if (typeof id === "number") { return id.toString().padStart(6, "0"); } return id;
} formatId(42); // "000042"
formatId("AB123"); // "AB123" // リテラル型(特定の値のみ許可)
type Direction = "north" | "south" | "east" | "west";
type Status = "pending" | "active" | "inactive"; function move(dir: Direction, steps: number): void { console.log(`${dir}へ${steps}歩進む`);
} move("north", 3); // OK
// move("up", 1); // エラー!"up" は Direction に含まれない
ジェネリクス(Generics)
ジェネリクスは「型を引数として受け取る」仕組みです。様々な型に対応しつつも型安全を保てる、TypeScriptの強力な機能です。
// ジェネリック関数
function identity<T>(value: T): T { return value;
} identity<string>("こんにちは"); // 型を明示
identity(42); // 型推論でnumber // 配列の最初の要素を返す
function first<T>(arr: T[]): T | undefined { return arr[0];
} first([1, 2, 3]); // number | undefined
first(["a", "b", "c"]); // string | undefined ジェネリックインターフェース
// APIレスポンスの型定義
interface ApiResponse<T> { data: T; status: number; message: string;
} interface User { id: number; name: string;
} // 使用例
const userResponse: ApiResponse<User> = { data: { id: 1, name: "田中" }, status: 200, message: "OK"
}; const usersResponse: ApiResponse<User[]> = { data: [{ id: 1, name: "田中" }, { id: 2, name: "鈴木" }], status: 200, message: "OK"
}; 型制約(extends)
// T は length プロパティを持つ型に制限
function getLength<T extends { length: number }>(item: T): number { return item.length;
} getLength("hello"); // 5(string)
getLength([1, 2, 3]); // 3(array)
// getLength(42); // エラー!numberはlengthを持たない Tの代わりにU、K、Vなども使われます(慣習的に)。実践的なTypeScriptのTips
型ガード(Type Guards)
ユニオン型の値の型を実行時に絞り込む手法です。
type Cat = { type: "cat"; meow(): void };
type Dog = { type: "dog"; bark(): void };
type Pet = Cat | Dog; function makeSound(pet: Pet): void { if (pet.type === "cat") { pet.meow(); // ここではCat型として扱われる } else { pet.bark(); // ここではDog型として扱われる }
} // instanceof を使った型ガード
function processInput(input: string | Date): string { if (input instanceof Date) { return input.toISOString(); } return input.toUpperCase();
} Readonly と as const
// Readonly でプロパティを読み取り専用に
interface Config { readonly apiUrl: string; readonly timeout: number;
} const config: Config = { apiUrl: "https://api.example.com", timeout: 3000
};
// config.apiUrl = "変更"; // エラー! // as const でオブジェクト全体を定数に
const COLORS = { primary: "#e8a040", secondary: "#f0c060", danger: "#e05040"
} as const; // COLORS.primary は "e8a040" のリテラル型になる Mapped Types と Utility Types
interface Product { id: number; name: string; price: number; description: string;
} // Partial: 全プロパティをオプショナルに
type PartialProduct = Partial<Product>;
// { id?: number; name?: string; ... } // Required: 全プロパティを必須に
type RequiredProduct = Required<PartialProduct>; // Pick: 一部のプロパティだけ取り出す
type ProductSummary = Pick<Product, "id" | "name" | "price">; // Omit: 一部のプロパティを除外
type ProductWithoutId = Omit<Product, "id">; // Record: キーと値の型を指定
type CategoryMap = Record<string, Product[]>; any型の多用はTypeScriptの恩恵を損ないます。型がわからない場合は unknown を使い、型ガードで絞り込む方法を検討しましょう。まとめ
TypeScriptの基礎から実践的なテクニックまで解説しました。重要なポイントを振り返りましょう:
- TypeScriptはJavaScriptのスーパーセットで、静的型付けによって安全なコードが書ける
- 型アノテーションと型推論を活用して、必要な箇所だけ型を明示的に指定する
- インターフェースでオブジェクトの形を定義し、クラスに実装できる
- ジェネリクスで型安全な汎用コンポーネントを作れる
- ユーティリティ型(Partial、Pick、Omitなど)で型の変換が簡単にできる
最初は型の記述が面倒に感じることもありますが、プロジェクトが大きくなるほどTypeScriptの恩恵を実感できます。まずは既存のJavaScriptプロジェクトに tsconfig.json を追加するところから始めてみましょう!