【TypeScript】オブジェクトの型を定義する方法

JavaScript

こんにちは、かつコーチです。

前回は配列とタプルの型の書き方を解説しました。

今回は、TypeScriptを使ううえで避けて通れない「オブジェクトの型定義」について解説します。

ユーザー情報や商品データなど、実務のコードではオブジェクトを扱う場面がとても多いです。

型をきちんと定義できるようになると、コードの安全性がぐっと上がるので、しっかり押さえていきましょう。

オブジェクトの型とは?

なぜオブジェクトに型が必要なのか

オブジェクト(複数のプロパティをまとめて持つデータのかたまり)は、JavaScriptの段階では自由に形を変えられてしまいます。

たとえば、ある場所では name というプロパティがあると思って書いたコードが、別の場所では name が存在しないオブジェクトを渡されてエラーになる、といったことが起こり得ます。

TypeScriptでオブジェクトの型を定義しておくと、「このオブジェクトにはどんなプロパティがあり、それぞれ何の型なのか」をコード上で保証できます。

存在しないプロパティにアクセスしようとした瞬間に、実行前(コンパイル時)にエラーとして教えてくれるのが大きなメリットです。

インラインで型を書く基本形

もっともシンプルな書き方は、変数の後ろに直接オブジェクトの型を書く方法です。

const user: { name: string; age: number } = {
  name: "かつコーチ",
  age: 30,
};

console.log(user.name); // "かつコーチ"

{ name: string; age: number } の部分がオブジェクトの型です。

プロパティ名の後ろにコロンを書き、その型を指定します。

区切りはセミコロンでもカンマでも動きますが、複数行で書くときはセミコロンを使うのが一般的です。

type文を使った型定義

type文で名前をつける

インラインで型を書くやり方は、同じ形のオブジェクトを何度も使う場合に不便です。

そこで使うのが type 文です。

type User = {
  name: string;
  age: number;
};

const user1: User = {
  name: "かつコーチ",
  age: 30,
};

const user2: User = {
  name: "田中",
  age: 25,
};

型に User という名前をつけておけば、同じ形のオブジェクトを何度でも再利用できます。

変数の型注釈も : User と書くだけになり、コードがすっきりします。

プロパティが増えたときのメリット

オブジェクトが持つプロパティが増えるほど、type で名前をつけるメリットは大きくなります。

type Product = {
  id: number;
  name: string;
  price: number;
  stock: number;
  category: string;
};

function displayProduct(product: Product): void {
  console.log(`${product.name}:${product.price}円(在庫${product.stock})`);
}

関数の引数に Product 型を指定しておけば、呼び出す側は「どんな形のオブジェクトを渡せばいいか」がすぐに分かります。

チームで開発するときは、この「型を見れば仕様が分かる」という状態がとても重要です。

オプショナルプロパティと読み取り専用プロパティ

? で任意のプロパティにする

すべてのプロパティが必ず入るとは限りません。

「あってもなくてもいい」プロパティには ? をつけます。

type User = {
  name: string;
  age: number;
  email?: string; // 省略可能
};

const user1: User = {
  name: "かつコーチ",
  age: 30,
}; // emailを省略してもエラーにならない

const user2: User = {
  name: "田中",
  age: 25,
  email: "tanaka@example.com",
};

email?: string と書くと、email プロパティを持たないオブジェクトも User 型として認められます。

省略された場合、user1.email の値は undefined になります。

readonly で書き換えを防ぐ

一度セットしたら変更してほしくないプロパティには、readonly をつけます。

type User = {
  readonly id: number;
  name: string;
};

const user: User = {
  id: 1,
  name: "かつコーチ",
};

user.name = "変更後の名前"; // OK
user.id = 2; // エラー:読み取り専用プロパティには代入できない

id のようにデータベースの主キーなど「作成後に変わってはいけない値」には readonly をつけておくと、うっかり書き換えるミスを事前に防げます。

つまずきやすいポイント:プロパティ不足のエラー

実際につまずいた話

私が実務でTypeScriptを使い始めたころ、フォームの入力値をAPIに送信する処理で、こんなエラーに何度も遭遇しました。

エラーメッセージは Property 'email' is missing in type '{ name: string; age: number; }' but required in type 'User'. というものです。

原因は単純で、User 型に必須プロパティとして定義していた email を、送信用のオブジェクトに入れ忘れていたことでした。

最初は「実行してみないと気づけないミスがコンパイル前に見つかった」という感覚がなく、むしろ「なんでこんなに厳しいんだ」と感じていました。

ですが、これがまさにTypeScriptの型チェックの効果です。

実行前にミスを見つけられたおかげで、本番環境に不完全なデータを送ってしまう事故を防げたわけです。

Before/Afterで確認する

❌ Before:必須プロパティを入れ忘れる

type User = {
  name: string;
  age: number;
  email: string;
};

function registerUser(user: User): void {
  console.log(`${user.name}さんを登録しました`);
}

// emailが抜けているためコンパイルエラーになる
registerUser({
  name: "かつコーチ",
  age: 30,
});

このコードは、User 型が要求している email プロパティが渡すオブジェクトに存在しないため、コンパイル時にエラーになります。

✅ After:すべての必須プロパティを渡す

type User = {
  name: string;
  age: number;
  email: string;
};

function registerUser(user: User): void {
  console.log(`${user.name}さんを登録しました`);
}

registerUser({
  name: "かつコーチ",
  age: 30,
  email: "katsu@example.com",
});

必須プロパティをすべて渡すことで、エラーが解消されます。

もし本当に省略していい値なら、型定義側で email?: string にするのが正しい対応です。

「エラーが出たから型を緩める」のではなく、「本当に必須なのか、省略可能なのか」を型定義に反映させることを意識しましょう。

応用:インデックスシグネチャで柔軟なオブジェクトを扱う

プロパティ名が事前に分からない場合

APIのレスポンスなどで、プロパティ名が動的に決まるオブジェクトを扱うこともあります。

そういった場合は「インデックスシグネチャ」という書き方を使います。

type Scores = {
  [subject: string]: number;
};

const scores: Scores = {
  国語: 80,
  数学: 90,
  英語: 75,
};

console.log(scores.国語); // 80
console.log(scores["理科"]); // undefined(存在しないプロパティ)

[subject: string]: number と書くことで、「プロパティ名は文字列なら何でもよく、値は必ず数値」という型を表現できます。

科目名が事前に決まっていない集計処理などで役立つ書き方です。

まとめ

この記事のポイント

  • オブジェクトの型は { プロパティ名: 型 } の形で定義する
  • 同じ形のオブジェクトを繰り返し使うときは type で名前をつけると再利用しやすい
  • ? で省略可能なプロパティ、readonly で読み取り専用のプロパティを表現できる
  • 必須プロパティの入れ忘れはコンパイル時にエラーとして検出される
  • プロパティ名が動的な場合はインデックスシグネチャを使う

次に読むべき記事

オブジェクトの型が扱えるようになったら、次は関数の型について学んでいきましょう。

→ 次の記事:関数の型:引数と戻り値に型をつける

タイトルとURLをコピーしました