type-challenges の Pick に挑戦した

# はじめに

type-challenges の Pick の問題に挑戦したので、自分の思考の整理も含め記事にしたいと思い、記事にすることにしました。

ちなみに、私はわからなかったので答えを見ましたが、偉そうに解説していきたいと思います。

問題は以下リンクです。

https://github.com/type-challenges/type-challenges/blob/main/questions/00004-easy-pick/README.ja.md

# 問題内容
以下のような問題です。

組み込みの型ユーティリティPick<T, K>を使用せず、TからKのプロパティを抽出する型を実装します。

例えば：

```tsx
interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
}
```

# 回答

回答は以下です。

```tsx
type MyPick<T, K extends keyof T> = { [P in K]: T[P]; }
```

# 分解しながら見ていく

## `MyPick<T, K extends keyof T>`

### `T`

これは元となるオブジェクトの型ですね。

この問題の中で言うと、以下の部分になります。

```tsx
interface Todo {
  title: string
  description: string
  completed: boolean
}
```

### `K extends keyof T`

こちらはもう少し分解してみていきたいと思います。

`K` 

これは、`T` 型から抽出したい `key` 名を表しています。

上で出した例を参考にすると、`title` や `description` などといった `key` 名が入ってきます

`extends`

TypeScript では `extends` キーワードをつけることでジェネリクスの型を特定の型に限定することができます。

今回の場合だと、ジェネリクスの型 `K` を `keyof T` という型に限定していることになります。

参考：

https://typescriptbook.jp/reference/generics/type-parameter-constraint

`keyof T` 

`keyof` はオブジェクトの型からプロパティ名を型として返す演算子です。

今回の問題では、`T` が `Todo` 型なので、`K` は以下のような型となります。（以下2行はイコールになるということです。）

```tsx
type TodoKey = keyof Todo;
type TodoKey = "title" | "description" | "completed";
```

参考：

https://typescriptbook.jp/reference/type-reuse/keyof-type-operator

このように `K extends keyof T` とすることで `K` は `T` のプロパティ名で構成された型であることを保証しています。

### `MyPick<T, K extends keyof T>` のまとめ

ここでは、`MyPick` 型が使用される時にどのような型を想定しているかが定義されていることがわかると思います。

具体的には、`'title'` と `'completed'` を取り出したい場合は、`MyPick<Todo, 'title' | 'completed' >` と定義されるということです。

## `{ [P in K]: T[P]; }`

### `[P in K]`

ここでは TypeScript で Mapped Types（マップ型）を使用して、既存の型を元に新しい型を作成しています。

Mapped Types については以下に記載があります。

https://typescriptbook.jp/reference/type-reuse/mapped-types

`P` 

一時的な変数名です。

`in K`

K の中に含まれる要素（キー）を1つずつ取り出して、その各要素に対して処理を行うという意味です。

つまり、`[P in K]` は `K` に含まれるキー全てに対して、 `P` という一時的な名前をつけてループするようなイメージです。

具体例

`K = ‘title’ | ‘completed’` とした場合、`P` が `title` と `completed` の2つの値を順番に取り出していきます。

### `T[P]`

`T`

元となるオブジェクト型、ここでいうと `Todo` 型です。

`T[P]`

「型 `T` のプロパティ `P` に対応する型」を指しています。`T` から `P` という名前のプロパティの型を取り出している、ということです。

具体例

`T = Todo` で `P = 'title'` なら `T['title']` は `string` 型、

`P = 'completed'` なら `T['completed']` は `boolean` 型となります。

### `{ [P in K]: T[P]; }` のまとめ

`{ [P in K]: T[P]; }` は「`K` に含まれる全てのキー `P` について、そのキー `P` に対応する型 `T[P]` を持つプロパティを持った新しいオブジェクト型を作る」という意味になります

具体的には、`K = 'title' | 'completed'` 、`T = Todo` の場合、次のようなオブジェクト型が作られます

```tsx
{
 title: string; // T['title']が string になる
 completed: boolean; // T['completed'] が boolean になる
}
```

## `type MyPick<T, K extends keyof T> = { [P in K]: T[P]; }`

最後に最終確認をしてみます。

以下の `Todo` 型から `title` と `completed` を取り出したいと仮定します。

```tsx
interface Todo {
 title: string;
 descriptiotn: string;
 completed: boolean;
}
```

取り出す時に以下のように定義することができます

```tsx
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
```

ここで、内部的に起こっていることは以下です。

- `K = 'title' | 'completed'`
- `[ P in K ]` によって、P はまず `'title'` になり、次に `'completed'` になります。
- `T[P]` は `T['title']` （これは `string` ）と `T['completed']` （これは `boolean` ）になります。

結果的に以下のような型となります。

```tsx
{
 title: string;
 completed: boolean;
}
```

# 最後に

初級の最初の問題でしたが、とても難しかったです。

まだまだ理解できていないところがたくさんあるなあと感じました。

また、次の問題にも挑戦してみたいと思います！