TypeScript | 基礎文法:オブジェクト基礎 – ネストしたオブジェクト型

TypeScript
スポンサーリンク

ネストしたオブジェクト型とは何か(まずはイメージから)

ネストしたオブジェクト型は、「オブジェクトの中に、さらにオブジェクトが入っている形を、そのまま型で表現したもの」です。
現実のデータって、ユーザーの中に住所があったり、住所の中に国や市があったりと、階層構造になりがちですよね。
TypeScript では、その階層構造をそのまま「型」として書けます。

const user = {
  name: "Taro",
  address: {
    country: "Japan",
    city: "Tokyo",
  },
};
TypeScript

この { ... } の形を、きちんと型として表現しておくと、
「country を書き忘れた」「city に number を入れた」みたいなミスをコンパイル時に止められるようになります。


一番基本の書き方:中にそのままオブジェクト型を書く

interface でネストしたオブジェクト型を書く

interface Person {
  name: string;
  address: {
    country: string;
    city: string;
  };
}

const person: Person = {
  name: "Taro",
  address: {
    country: "Japan",
    city: "Tokyo",
  },
};
TypeScript

ここでやっていることはシンプルで、address プロパティの型として、
さらに { country: string; city: string } というオブジェクト型を書いているだけです。

TypeScript はこう理解します。

  • name は string
  • address は「country: stringcity: string を持つオブジェクト」

だから、次のようなコードはエラーになります。

const badPerson: Person = {
  name: "Taro",
  address: {
    country: "Japan",
    // city がない → エラー
  },
};
TypeScript

「ネストしていても、結局は“プロパティ名: 型”の組み合わせが入れ子になっているだけ」
この感覚を持てると、一気に怖くなくなります。

type エイリアスでもまったく同じことができる

type Address = {
  country: string;
  city: string;
};

type Person = {
  name: string;
  address: Address;
};
TypeScript

address の型として、別で定義した Address 型を使っています。
interface でも type でも、ネストの表現の仕方は同じです。


ネストを分割して書くか、その場で書くか

その場で書くパターン(小さいとき・一度きりのとき)

type User = {
  id: number;
  profile: {
    nickname: string;
    bio: string;
  };
};
TypeScript

profile はここでしか使わないし、そんなに大きくない」
こういうときは、その場で { ... } と書いてしまっても読みやすいです。

分割して名前をつけるパターン(再利用したいとき・大きくなってきたとき)

type Profile = {
  nickname: string;
  bio: string;
};

type User = {
  id: number;
  profile: Profile;
};
TypeScript

Profile を別名として切り出すことで、

  • 他の型でも Profile を再利用できる
  • 「ここにはプロフィールが入るんだな」と意味が伝わりやすくなる

というメリットが出てきます。

「ネストが深くなってきた」「同じ形があちこちに出てきた」と感じたら、
「一段切り出して名前をつける」のが良いタイミングです。


ネストしたオブジェクト型と optional / readonly

ネストの中にも optional を使える

type Address = {
  country: string;
  city: string;
  building?: string;
};

type User = {
  name: string;
  address?: Address;
};
TypeScript

ここでは、

  • building? は「建物名はあってもなくてもいい」
  • address? は「住所自体がないユーザーもいる」

という仕様を、そのまま型にしています。

実際の値としては、こんなパターンが全部 OK です。

const u1: User = { name: "Taro" };

const u2: User = {
  name: "Hanako",
  address: {
    country: "Japan",
    city: "Osaka",
  },
};

const u3: User = {
  name: "Ken",
  address: {
    country: "Japan",
    city: "Nagoya",
    building: "Some Building 101",
  },
};
TypeScript

ネストしたオブジェクトでも、「どの階層のどのプロパティが必須で、どこが任意か」を細かく設計できます。

ネストの中にも readonly を使える

type Address = {
  readonly country: string;
  city: string;
};

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

ここでは、

  • id は一度決めたら変えない
  • country も一度決めたら変えない(国が変わることはない想定)
  • cityname は変わってもよい

というルールを型で表現しています。

ネストしていても、「どこを固定して、どこを変えていいか」をプロパティ単位でコントロールできます。


ネストが深くなったときに意識してほしいこと

「階層ごとに“意味のある塊”として型を切る」

たとえば、こんなデータ構造を考えてみます。

const data = {
  user: {
    id: 1,
    name: "Taro",
    address: {
      country: "Japan",
      city: "Tokyo",
    },
  },
  meta: {
    createdAt: "2025-01-01",
    updatedAt: "2025-01-02",
  },
};
TypeScript

これをそのまま1つの型で書くこともできますが、
読みやすさと再利用性を考えると、階層ごとに分けた方がきれいです。

type Address = {
  country: string;
  city: string;
};

type User = {
  id: number;
  name: string;
  address: Address;
};

type Meta = {
  createdAt: string;
  updatedAt: string;
};

type Response = {
  user: User;
  meta: Meta;
};
TypeScript

こうしておくと、

  • User だけを別の関数の引数に使える
  • Meta を他のレスポンス型でも使い回せる
  • 「この型は何を表しているのか」が名前から伝わる

という状態になります。

ネストしたオブジェクト型は、「全部を1行で書く」ものではなく、
「意味のある階層ごとに分けて、名前をつけていく」ための土台だと捉えてほしい。


初心者がまず掴んでおきたい「ネストしたオブジェクト型」の感覚

ネストしたオブジェクト型の本質は、とてもシンプルです。

  • オブジェクトの中に、さらに「プロパティ名: 型」の組み合わせが入っているだけ
  • それをそのまま { ... } で書くか、別の型として切り出して名前をつけるかの違いだけ
  • optional や readonly も、ネストの中で普通に使える

だから、まずはこう考えてみてください。

「このデータ、現実世界ではどういう“入れ子構造”になっている?」
「ユーザーの中に住所があって、住所の中に国と市があるよね」

その構造を、そのまま型にしていく。
途中で「この塊には名前をつけた方が分かりやすいな」と思ったら、typeinterface に切り出す。

そうやって少しずつ「構造を型に写し取る」感覚が育ってくると、
ネストしたオブジェクト型は、ただの記法ではなく、
「現実のデータ構造をそのまま安全に扱うための設計図」として見えてきます。

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