TypeScript | 基礎文法:オブジェクト基礎 – オブジェクトの分割代入

TypeScript
スポンサーリンク

オブジェクトの分割代入って何をしているのか

まずイメージからいきましょう。
オブジェクトの分割代入は、「オブジェクトの中から、欲しいプロパティだけを“取り出して”変数にする書き方」です。

const user = {
  name: "Taro",
  age: 20,
};

const { name, age } = user;

console.log(name); // "Taro"
console.log(age);  // 20
TypeScript

{ name, age } = user という書き方で、
user.namename という変数に」「user.ageage という変数に」取り出しています。

TypeScript 的には、「オブジェクトの型から、プロパティごとの型をちゃんと推論してくれる便利な取り出し方」だと思ってください。


一番基本の分割代入と型推論

シンプルなオブジェクトから取り出す

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

const user: User = {
  name: "Taro",
  age: 20,
};

const { name, age } = user;
TypeScript

このとき、TypeScript はこう推論します。

  • name の型は string
  • age の型は number

つまり、次のようなことが起きます。

name.toUpperCase(); // OK(string のメソッド)
age.toFixed(1);     // OK(number のメソッド)

// name = 123;      // エラー: Type 'number' is not assignable to type 'string'.
// age = "20";      // エラー: Type 'string' is not assignable to type 'number'.
TypeScript

ポイントは、「分割代入しても型情報はちゃんと保たれる」ということです。
user に型が付いていれば、その中から取り出した変数にも正しい型が付きます。


変数名を変えて取り出す(別名をつける)

プロパティ名と違う変数名を使いたいとき

ときどき、「プロパティ名は name だけど、変数名は userName にしたい」みたいな場面があります。
そんなときは、こう書きます。

const user = {
  name: "Taro",
  age: 20,
};

const { name: userName, age: userAge } = user;

console.log(userName); // "Taro"
console.log(userAge);  // 20
TypeScript

name: userName は、
user.nameuserName という変数に入れる」という意味です。

TypeScript 的には、

  • userName の型は string
  • userAge の型は number

と推論されます。

「左側の name は“どのプロパティを取るか”、右側の userName は“変数名”」
この対応関係を意識して読むと、すぐ慣れます。


デフォルト値付きの分割代入と型

プロパティがないときに使う「デフォルト値」

オブジェクトにそのプロパティがない場合に備えて、デフォルト値を設定することもできます。

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

const user: User = {
  name: "Taro",
};

const { age = 18 } = user;

console.log(age); // 18
TypeScript

ここで age は、「number 型として扱われる」ことが多いです。
age プロパティがあればその値が入り、なければ 18 が入ります。

重要なのは、「optional なプロパティを分割代入するときは、“ないかもしれない”ことを意識する」ということです。
デフォルト値を付けるのは、「ないときの振る舞い」を明示する一つの方法です。


ネストしたオブジェクトの分割代入

一段ネストしたプロパティを取り出す

ネストしたオブジェクトでも、分割代入はそのまま使えます。

type User = {
  name: string;
  address: {
    city: string;
    zip: string;
  };
};

const user: User = {
  name: "Taro",
  address: {
    city: "Tokyo",
    zip: "100-0001",
  },
};

const {
  address: { city, zip },
} = user;

console.log(city); // "Tokyo"
console.log(zip);  // "100-0001"
TypeScript

ここで起きていることは、

  • user.address.citycity という変数に
  • user.address.zipzip という変数に

取り出しているだけです。

TypeScript は、User の型からさらに address の型を辿って、
city: string, zip: string だとちゃんと理解してくれます。

ネスト部分に別名をつける

const {
  address: { city: userCity, zip: userZip },
} = user;
TypeScript

こう書けば、

  • userCity の型は string
  • userZip の型は string

として扱われます。

ネストが深くなっても、「プロパティの構造に沿って書いているだけ」だと捉えると、怖さが減ります。


残りをまとめて受け取る(rest構文との組み合わせ)

一部だけ取り出して、残りをまとめる

分割代入は、「一部だけ取り出して、残りをまとめて受け取る」こともできます。

const user = {
  name: "Taro",
  age: 20,
  email: "taro@example.com",
};

const { name, ...rest } = user;

console.log(name); // "Taro"
console.log(rest); // { age: 20, email: "taro@example.com" }
TypeScript

ここで rest の型は、ざっくりこう推論されます。

{
  age: number;
  email: string;
}
TypeScript

つまり、「元のオブジェクトの型から、取り出したプロパティを除いた残りの型」になります。

この書き方は、「一部だけ個別の変数にして、残りはまとめて次の処理に渡したい」ときにとても便利です。


関数の引数での分割代入と型

引数でそのまま分割する

関数の中でよく使うのが、「引数をその場で分割代入する」書き方です。

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

function printUser({ name, age }: User) {
  console.log(`${name} (${age})`);
}

const user: User = { name: "Taro", age: 20 };

printUser(user);
TypeScript

ここでは、

  • 引数の型は User
  • その User から nameage を分割代入で取り出している

という構造になっています。

関数の中で user.nameuser.age と書かなくてよくなるので、
「この関数は User のどのプロパティを使っているのか」が一目で分かるようになります。

別名やデフォルト値も組み合わせられる

function printUser({ name: userName, age = 18 }: User) {
  console.log(`${userName} (${age})`);
}
TypeScript

このように、

  • nameuserName という変数名で受け取り
  • age がなければ 18 を使う

といった柔軟な受け取り方も、型付きのまま書けます。


分割代入と型を扱うときに意識してほしいこと

オブジェクトの分割代入は、見た目が少しトリッキーに感じるかもしれませんが、
やっていることは一貫してシンプルです。

「このオブジェクトの、このプロパティを、この変数名で取り出したい」
それを、短く・宣言的に書くための記法です。

TypeScript はそこに対して、

  • 元のオブジェクトの型から、プロパティごとの型をちゃんと推論し
  • optional なプロパティなら「ないかもしれない」ことを意識させ
  • 関数の引数でも同じように型安全に扱えるようにしてくれます。

なので、コードを書くときはこう考えてみてください。

「この関数、本当は user.nameuser.age しか使ってないよな」
「だったら、引数のところで { name, age }: User と分割してしまおう」

そうすると、「どのデータを使っているのか」が型とコードの両方からハッキリ見えるようになります。
それが、分割代入と型を組み合わせる一番おいしいところです。

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