TypeScriptで型定義を再利用してインターフェースの一部の任意プロパティを必須プロパティに変換する方法

## 導入
今日の話題はTypeScriptで型定義を再利用してインターフェースの一部の任意プロパティを必須プロパティに変更したりする方法についてです。この逆の一部の必須プロパティを任意プロパティに変更するのはここで紹介する方法の応用なので省略します。

:::note info
※ 私が株式会社愛宕 Advent Calendar 2023に書く記事は主に社内向けに共有しておきたいけど勉強会をするまでもないちょっとしたTipsにしたいと思います。
:::

ここで、必須プロパティとは次の例のように定義されたインターフェース内の`name`や`type`で、任意プロパティとは`age`や`homeland`を指します。

```TypeScript
interface Player {
  name: string
  type: string
  age?: number
  homeland?: string 
}
```

TypeScriptではユーティリティ型と呼ばれる既存の型から別の型を生み出す型が存在します。型から型を生み出すということは型を変換するともいえ、また型を導き出す関数とみなすこともできます。

TypeScriptのビルトインパッケージで定義されていて`import`なしに利用できるユーティリティ型として、全てのプロパティを必須にする`Required<T>`があります。全てのプロパティを任意にするのは`Partial<T>`です。
`<T>`の`T`の部分には既存の型が入ります。つまり、

```TypeScript
type PlayerAllPropertyRequired = Required<Player>
type PlayerAllPropertyOptional = Partial<Player>
```

と書けば新しく定義された`PlayerAllPropertyRequired`の`age`と`homeland`は`?`が取れて必須となり、`PlayerAllPropertyOptional`の`name`と`type`は`?`がついて任意となります。

ここまでは簡単です。問題は`age`だけ必須にしたり、`name`だけ任意にしたりする方法が意外と難しいことです。

## 解決
あるインターフェースの一部のプロパティを必須にするには、次のようなユーティリティ型を書くといいでしょう。

```TypeScript
type RequiredProperties<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
```

使い方は、

```TypeScript
type PlayerSomePropertyRequired = RequiredProperties<Player, "name" | "type" | "age">
```

となります。この例の場合、`name`と`type`は元々必須なので書いても書かなくても同じ結果となります。

## 解説
どういう仕組みなのか解説します。

まず、`K`の後についている`extends keyof T`という部分がわかりにくいですが、これは`K`が満たすべき制約を示しています。
ユーティリティ型を型の関数だとみなすと`K`が（型）変数で`keyof T`が（型）変数`K`の型と言ってもいいです。

`keyof T`は`T`のプロパティをリテラル型として定義し全て結合してユニオン型としたものです。
例えば、`keyof Player`は`"name" | "type" | "age" | "homeland"`となります。

これを使って`K`の部分を書き直すと、`K extends "name" | "type" | "age" | "homeland"`となります。
`extends`は指定した型のサブタイプを要求するという制約で、今回はユニオン型に対してなので`"name" | "type"`や`"type" | "homeland"`などユニオン型に含まれる要素だけで作ったユニオン型を必要とするという意味になります。

まとめると、型定義の`RequiredProperties<T, K extends keyof T>`は、
- 型`T`と型`K`から新しい型を作る
- ただし、
  - `T`は何でもよい
  - `K`は`T`の全てのプロパティ名の中から任意のプロパティ名を組み合わせたユニオン型である必要がある

という意味になります。

次に定義内容の部分です。`Omit<T, K>`と`Pick<T, K>`はどちらも`Required<T>`と同じようにビルトイン型で
- `Omit<T, K>`: 型`T`のプロパティのうち型`K`に含まれるプロパティを削除した新しい型を定義する
- `Pick<T, K>`: 型`T`のプロパティのうち型`K`に含まれるプロパティだけを取り出した新しい型を定義する

といったものです。

これを踏まえて`Omit<T, K> & Required<Pick<T, K>>`を`&`で前後に分解して考えると、
- `Omit<T, K>`: 型`T`のプロパティのうち型`K`に含まれるプロパティを削除した型
- `Required<Pick<T, K>>`: 型`T`のプロパティのうち型`K`に含まれるプロパティだけを取り出した型で全てのプロパティが必須

となります。

最後にまとめると、`&`は型のマージなので、型`T`から型`K`に含まれるプロパティを一旦削除しておいて、削除したプロパティだけでできた型の全てのプロパティを必須とする型とマージするということになります。

例えば、
```
RequiredProperties<Player, "name" | "type" | "age">
```
は、`Omit<T, K> => Omit<Player, "name" | "type" | "age">`の部分で、
```TypeScript
interface OmittedPlayer {
  homeland?: string 
}
```
となり、

`Pick<T, K> => Pick<Player, "name" | "type" | "age">`の部分で、
```TypeScript
interface PickedPlayer {
  name: string
  type: string
  age?: number
}
```
となり、さらに`Required`がつくことで、

```TypeScript
interface RequiredPickedPlayer {
  name: string
  type: string
  age: number
}
```
のように`age`の`?`が取れて、最後に
```TypeScript
interface OmittedPlayer {
  homeland?: string 
}
```
とマージすることで

```TypeScript
interface PlayerSomePropertyRequired {
  name: string
  type: string
  age: number
  homeland?: string 
}
```
が得られます。