<CodeLearn/>
TypeScript レッスン3

インターフェース

オブジェクトの型を定義して、構造を明確にしよう

interfaceの基本

interfaceは オブジェクトの「形」を定義する仕組みです。 どんなプロパティがあり、それぞれがどんな型かを記述します。 TypeScriptでは、オブジェクトの構造が一致すれば型チェックを通過します(構造的部分型)。

// interface でオブジェクトの型を定義
interface User {
  id: number;
  name: string;
  email: string;
}

// この型に合うオブジェクトを作成
const user: User = {
  id: 1,
  name: "太郎",
  email: "taro@example.com",
};

// プロパティが足りないとエラー
// const bad: User = { id: 1, name: "太郎" };
// Error: Property 'email' is missing

// 余分なプロパティもエラー(直接代入時)
// const bad2: User = { id: 1, name: "太郎", email: "...", age: 25 };
// Error: Object literal may only specify known properties

オプショナルプロパティとreadonly

プロパティを任意にしたり、変更不可にしたりできます。

interface BlogPost {
  readonly id: number;       // 変更不可
  title: string;
  body: string;
  tags?: string[];           // オプショナル(あってもなくてもOK)
  publishedAt?: Date;        // オプショナル
  readonly createdAt: Date;  // 変更不可
}

const post: BlogPost = {
  id: 1,
  title: "TypeScript入門",
  body: "TypeScriptは素晴らしい...",
  createdAt: new Date(),
  // tags と publishedAt は省略OK
};

// readonlyプロパティは変更できない
// post.id = 2;        // Error: Cannot assign to 'id' because it is a read-only property
// post.createdAt = new Date(); // Error

// 通常のプロパティは変更OK
post.title = "TypeScript完全入門";

// オプショナルプロパティの安全なアクセス
const tagCount = post.tags?.length ?? 0;  // 0
const firstTag = post.tags?.[0] ?? "なし"; // "なし"

メソッドとインデックスシグネチャ

interfaceにはメソッドの型定義や、動的なキーの型定義も含められます。

// メソッドを持つinterface
interface Calculator {
  value: number;
  add(n: number): Calculator;      // メソッド構文
  subtract: (n: number) => Calculator;  // プロパティ構文
  reset(): Calculator;
  getResult(): number;
}

// インデックスシグネチャ: 任意のキーを許容
interface StringMap {
  [key: string]: string;  // 任意の文字列キーで string 値
}

const translations: StringMap = {
  hello: "こんにちは",
  goodbye: "さようなら",
  thanks: "ありがとう",
  // 何個でも追加OK
};

// 固定プロパティ + インデックスシグネチャ
interface Config {
  version: number;          // 必須
  debug: boolean;           // 必須
  [key: string]: unknown;   // その他は何でもOK
}

const config: Config = {
  version: 1,
  debug: true,
  apiUrl: "https://api.example.com",
  timeout: 3000,
};

interfaceの拡張(extends)

extendsを使って、 既存のinterfaceを拡張(継承)できます。コードの再利用性が高まります。

// 基本のinterface
interface Animal {
  name: string;
  age: number;
}

// 拡張: Animal のプロパティを継承 + 追加
interface Dog extends Animal {
  breed: string;
  isGoodBoy: boolean;
}

// Dog は name, age, breed, isGoodBoy を持つ
const shiba: Dog = {
  name: "ポチ",
  age: 3,
  breed: "柴犬",
  isGoodBoy: true,
};

// 複数のinterfaceを同時に拡張
interface Timestamps {
  createdAt: Date;
  updatedAt: Date;
}

interface SoftDelete {
  deletedAt?: Date;
  isDeleted: boolean;
}

// 複数の interface を extends で合成
interface Article extends Timestamps, SoftDelete {
  id: number;
  title: string;
  body: string;
  author: string;
}

// Article は全プロパティを持つ
const article: Article = {
  id: 1,
  title: "TypeScript入門",
  body: "...",
  author: "太郎",
  createdAt: new Date(),
  updatedAt: new Date(),
  isDeleted: false,
};

// interface の宣言マージ(同名で追加定義)
interface Window {
  myCustomProp: string;
}
// 既存の Window interface に myCustomProp が追加される

オブジェクトの構造を実践する

interfaceの考え方をJavaScriptで実践してみましょう。 オブジェクトの構造をバリデーションする関数を作ります。

index.htmllazy
1 lines0 issues

Monaco Editor を準備しています

表示領域に入った時点で Monaco と Shiki を初期化します。

preview.local
Live Preview
Console

console.log / warn / error の出力がここに表示されます。

type vs interface

typeinterfaceは 似ていますが、いくつかの違いがあります。

// === interface でできて type でできないこと ===

// 宣言マージ(同名で追加)
interface User {
  name: string;
}
interface User {
  age: number;
}
// User は { name: string; age: number; } になる

// type では同名の再宣言はエラー
// type Point = { x: number; };
// type Point = { y: number; }; // Error!


// === type でできて interface でできないこと ===

// プリミティブ型のエイリアス
type ID = string | number;

// ユニオン型
type Status = "active" | "inactive" | "pending";

// タプル型
type Pair = [string, number];

// マップ型
type Readonly<T> = { readonly [P in keyof T]: T[P] };


// === どちらを使うべき? ===

// オブジェクトの型定義 → interface が推奨
//   - 拡張しやすい (extends)
//   - エラーメッセージがわかりやすい
//   - 宣言マージでライブラリの型を拡張できる

// ユニオン型、プリミティブ、関数型 → type を使う
//   - interface では表現できない

// チームで統一することが大切!

まとめ

  • interface でオブジェクトの構造を定義
  • ? でオプショナルプロパティ、readonly で変更不可
  • extends で既存のinterfaceを拡張
  • インデックスシグネチャで動的なキーに対応
  • 同名interfaceは自動マージされる
  • オブジェクト型は interface、その他は type を使うのが一般的