さとまたwiki

インターフェース

オブジェクトの形を定義する強力な機能

インターフェースとは?

日常での例え:設計図・仕様書

建築の設計図

「この建物には玄関、リビング、寝室が必要」
と構造を決める

interface

「このオブジェクトにはname, age, emailが必要」
と構造を決める

なぜinterfaceが必要?

毎回同じ型を書くのは大変:

  • ユーザー情報を使う場所が10箇所あったら、10回書く?
  • プロパティを追加したら、10箇所全部直す?

interfaceなら:1箇所で定義、どこでも使い回せる!

基本的な使い方

interfaceの定義

オブジェクトの形を定義

typescript
// interfaceを定義
interface User {
  id: number;
  name: string;
  email: string;
}

// interfaceを使う
const user: User = {
  id: 1,
  name: "田中太郎",
  email: "tanaka@example.com"
};

// 関数の引数にも使える
function greetUser(user: User) {
  console.log(`こんにちは、${user.name}さん!`);
}

// 配列の型にも使える
const users: User[] = [user];
プレビュー
interface User {
id: number
name: string
}

オプショナルプロパティ

あってもなくてもいいプロパティ

typescript
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;      // ? = オプショナル
  phone?: string;    // なくてもOK
}

// ageとphoneは省略可能
const user1: User = {
  id: 1,
  name: "田中",
  email: "tanaka@example.com"
};

// あってもOK
const user2: User = {
  id: 2,
  name: "鈴木",
  email: "suzuki@example.com",
  age: 30
};
プレビュー
age?: number
// ? をつけると省略可能に

読み取り専用プロパティ

変更を禁止する

typescript
interface Config {
  readonly apiKey: string;
  readonly baseUrl: string;
  timeout: number;
}

const config: Config = {
  apiKey: "abc123",
  baseUrl: "https://api.example.com",
  timeout: 5000
};

// readonlyは変更できない
config.apiKey = "xyz";  // ❌ エラー!

// readonlyじゃないプロパティは変更OK
config.timeout = 10000; // ✅ OK
プレビュー
readonly apiKey: string
// 一度設定したら変更禁止

関数を含むinterface

メソッドの定義

関数もプロパティとして定義

typescript
interface Calculator {
  // 書き方1: メソッド記法
  add(a: number, b: number): number;

  // 書き方2: プロパティ記法
  subtract: (a: number, b: number) => number;
}

const calc: Calculator = {
  add(a, b) {
    return a + b;
  },
  subtract: (a, b) => a - b
};

console.log(calc.add(5, 3));      // 8
console.log(calc.subtract(5, 3)); // 2
プレビュー
// メソッドも型定義できる
add(a, b): number
subtract: (a, b) => number

interfaceの拡張(extends)

日常での例え:基本プランとオプション追加

「基本プラン」に「プレミアムオプション」を追加して「プレミアムプラン」に

extendsは既存のinterfaceに機能を追加します

extendsで拡張

既存のinterfaceを継承

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

// Animalを拡張してDogを作る
interface Dog extends Animal {
  breed: string;  // 犬種を追加
  bark(): void;   // メソッドも追加
}

const dog: Dog = {
  name: "ポチ",
  age: 3,
  breed: "柴犬",
  bark() {
    console.log("ワンワン!");
  }
};

// Dogはname, age, breed, barkすべて必要
プレビュー
interface Dog extends Animal
// AnimalのプロパティをすべてDogを含む

複数のinterfaceを拡張

複数を組み合わせる

typescript
interface Named {
  name: string;
}

interface Aged {
  age: number;
}

interface Emailable {
  email: string;
}

// 複数を同時に拡張
interface User extends Named, Aged, Emailable {
  id: number;
}

const user: User = {
  id: 1,
  name: "田中",
  age: 25,
  email: "tanaka@example.com"
};
プレビュー
extends A, B, C
// 複数のinterfaceを組み合わせ

interface vs type

どっちを使う?

基本的にはどちらでもOK!ほぼ同じことができます。

チームで統一するか、以下の使い分けを参考に:

interface が向いている

  • オブジェクトの形を定義
  • クラスで実装(implements)
  • 拡張(extends)を多用する
  • 宣言マージが必要

type が向いている

  • Union型(A | B)
  • プリミティブ型のエイリアス
  • タプル型
  • 複雑な型の演算

同じことができる例

どちらでも書ける

typescript
// interface
interface User1 {
  name: string;
  age: number;
}

// type
type User2 = {
  name: string;
  age: number;
};

// どちらも同じように使える
const user1: User1 = { name: "田中", age: 25 };
const user2: User2 = { name: "鈴木", age: 30 };
プレビュー
// オブジェクトの型定義なら
// interfaceでもtypeでもOK

typeでしかできないこと

Union型やプリミティブ型

typescript
// Union型はtypeで
type Status = "pending" | "approved" | "rejected";

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

// タプルはtypeで
type Point = [number, number];

// これらはinterfaceでは書けない
プレビュー
type Status = "A" | "B"
// Union型はtypeを使う

interfaceでしかできないこと

宣言マージ

typescript
// 宣言マージ: 同名のinterfaceが合体する
interface Window {
  myCustomProperty: string;
}

// 既存のWindow型に追加される
// ライブラリの型を拡張する時に便利

// typeは同名で再定義するとエラー
type MyType = { a: string };
type MyType = { b: number };  // ❌ エラー!
プレビュー
// interfaceは同名で追加できる
// (宣言マージ)

実践的な使い方

APIレスポンスの型

実際のプロジェクトでよく使う

typescript
// APIレスポンスの型定義
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

interface User {
  id: number;
  name: string;
  email: string;
}

// 使用例
async function fetchUser(id: number): Promise<ApiResponse<User>> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// 型安全にアクセス
const result = await fetchUser(1);
console.log(result.data.name);  // 補完が効く!
プレビュー
// APIの型を定義すると
// レスポンスの型が保証される

Reactコンポーネントのprops

フロントエンド開発で頻出

typescript
// Reactコンポーネントのprops型
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary";
  disabled?: boolean;
}

function Button({ label, onClick, variant = "primary", disabled }: ButtonProps) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={variant}
    >
      {label}
    </button>
  );
}

// 使用時に型チェック
<Button label="送信" onClick={() => {}} />  // ✅
<Button label={123} onClick={() => {}} />   // ❌ エラー
プレビュー
// propsの型を定義すると
// コンポーネントが安全に使える