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 を追加するところから始めてみましょう!