【TypeScript】高度なオブジェクト型まとめ

プログラミングTypeScriptを読んでいると、
高度なオブジェクト型というものがでてきました。

今回は、この高度なオブジェクト型についてまとめていきます。

# ルックアップ型
ルックアップ型は、オブジェクトのプロパティにアクセスし、その型を取得する機能です。\
オブジェクト型のプロパティ型を再利用したい場合などに使用します。

構文は `T[K]` で、`T` はオブジェクト型、`K` はそのプロパティのキーです。

```ts: 使用例
type User = {
  id: number;
  name: string;
  email: string;
};

// "name" プロパティの型を取り出す
type UserName = User['name']; // string

const userName: UserName = "Alice"; // OK
// const userName: UserName = 123; // エラー: string 型が必要
```

```ts: 応用例（ユニオン型との組み合わせ）
type User = {
  id: number;
  name: string;
  email: string;
};

type UserProperties = User['id' | 'name']; // number | string

const property1: UserProperties = 42;    // OK
const property2: UserProperties = "Bob"; // OK
// const property3: UserProperties = true; // エラー: number | string 型でない
```

# レコード型
レコード型は、キーと値の型を指定して一貫性のあるオブジェクト型を作成するために使用されます。
マップ型やディクショナリ型を表現する場合などに使用されます。

構文は `Record<K, T>` で、`K` はキーの型、`T` は値の型です。

```ts: 使用例
type UserRole = 'admin' | 'editor' | 'viewer';
type Permissions = Record<UserRole, boolean>;

const userPermissions: Permissions = {
  admin: true,
  editor: false,
  viewer: true,
};

// const invalidPermissions: Permissions = {
//   admin: true,
//   editor: false,
//   guest: true, // エラー: "guest" は UserRole 型ではない
// };
```

```ts: 応用例（ネスト構造）
type UserRole = 'admin' | 'editor' | 'viewer';
type PermissionDetails = {
  canEdit: boolean;
  canDelete: boolean;
};
type Permissions = Record<UserRole, PermissionDetails>;

const userPermissions: Permissions = {
  admin: { canEdit: true, canDelete: true },
  editor: { canEdit: true, canDelete: false },
  viewer: { canEdit: false, canDelete: false },
};
```

# マップ型
マップ型は、既存の型を基に新しい型を作成するために使用されます。
キーごとに型の変更を行ったり、新しいプロパティを追加する場合などに使用されます。

構文は `[K in Keys]: T` で、`K` はキー、`T` は値の型です。

```ts: 使用例
type User = {
  id: number;
  name: string;
};

// 全てのプロパティをオプショナルにする
type OptionalUser = {
  [K in keyof User]?: User[K];
};

const user: OptionalUser = { id: 1 }; // OK
// const user: OptionalUser = { age: 25 }; // エラー: "age" は存在しない
```

```ts: 応用例（読み取り専用プロパティに変換）
type User = {
  id: number;
  name: string;
};

type ReadonlyUser = {
  readonly [K in keyof User]: User[K];
};

const user: ReadonlyUser = { id: 1, name: "Alice" };
// user.id = 2; // エラー: 読み取り専用プロパティ
```

## 組み込みのマップ型
TypeScriptでは、多くのマップ型があらかじめ組み込まれています。
上述のレコード型もそのうちの1つです。

### Record\<T, K>
上述の通り、キーと値の型を指定して一貫性のあるオブジェクト型を作成するために使用されます。

### Partial\<T>
`Partial<T>` は、オブジェクト型 `T` のすべてのプロパティをオプショナルにします。

```ts
interface Person {
  name: string;
  age: number;
}

type PartialPerson = Partial<Person>;
// 結果: { name?: string; age?: number; }
```

### Required\<T>
`Required<T>` は、オブジェクト型 `T` のすべてのプロパティを必須にします。

```ts
interface Config {
  name?: string;
  timeout?: number;
}

type RequiredConfig = Required<Config>;
// 結果: { name: string; timeout: number; }
```

### Readonly\<T>
`Readonly<T>` は、オブジェクト型 `T` のすべてのプロパティを読み取り専用にします。

```ts
interface Mutable {
  x: number;
  y: number;
}

type Immutable = Readonly<Mutable>;
// 結果: { readonly x: number; readonly y: number; }
```

### Pick\<T, K>
`Pick<T>` は、オブジェクト型 `T` から指定したプロパティ `K` のみを選択して新しい型を作成します。

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

type UserInfo = Pick<User, "name" | "email">;
// 結果: { name: string; email: string; }
```

# おわりに
今回紹介した高度な型を使用することで、
再利用性や、型定義の柔軟性も向上するので、
使用できる場面ではどんどん適宜使っていきたいです。

それでは。

# 参考文献
https://www.oreilly.co.jp//books/9784873119045/

https://refine.dev/blog/typescript-record-type/#introduction

https://zenn.dev/pokotyan/articles/fd47f277ed80c0

https://tsuboi99553758.hatenablog.com/entry/2020/09/15/121153

https://kitamura.daa.jp/web/ts_types/

https://typescriptbook.jp/reference/builtin-api/map