😸

type-challenges の 1 問目を丁寧に解く

2024/12/09に公開

はじめに

この記事は、GLOBIS Advent Calendar 2024 の 7 日目の記事です。

筆者はここ数年は主に TypeScript を使って開発をしているのですが、その型システムを深淵を覗くために、type-challenges を(途中まで)解いたことがあります。

主観ですが、Easy の中では一番最初の問題が一番難しかった記憶があります。n 番煎じにはなりますが、今日はこの最初の問題を丁寧に見ていこうと思います。

task

type-challenges/questions/00004-easy-pick/README.md at main · type-challenges/type-challenges

Implement the built-in Pick<T, K> generic without using it.
Constructs a type by picking the set of properties K from T
For example:

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = MyPick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

solution

まず最初に、期待されていることを確認します。期待値は、MyPickという型を実装することです。MyPickは TypeScript 組み込みのPick[1]と同じ振る舞いをすればよいということなので、まずはPickのドキュメントを確認するのがいいでしょう。

やるべきことはわかったので、一歩ずつこの型を完成させていきます。

type MyPick<T, K> = any;

まず、Tに注目してみます。Tは特に制約はなさそうです。

次に、Kに注目してみます。KTのプロパティ名だけを受け付ける必要がありそうです。型引数に制約をつけるにはどうしたらいいでしょうか。

ここで登場するのが、extends[2]です。

type MyPick<T, K extends ...> = any;

では何をextendsすればいいでしょうか。先程の繰り返しになりますが、Tのプロパティ名ですね。ではTのプロパティを列挙したい場合はどうすればいいでしょうか。

次に登場するのが、keyof[3]です。keyofを使えば、Tのプロパティ名をユニオン型[4]で手に入れることができます。

type MyPick<T, K extends keyof T> = any;

ここまでで左辺は埋まったので、残りの右辺を考えていきます。

Kはユニオン型になっているので、これをキー制約として使えれば良さそうです。ではこれを実現するにはどうすればいいかというと、Mapped Types[5]を使うことができます。

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

ここまで来ればあと一息、といった感じです。Kの要素をPとして取り出しているので、これを使ってTから取り出していきます。ここでは、インデックスアクセス型[6]を使うことでこれが実現できます。

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

これで完成です。

Easy の 1 問目ですが、extendskeyof、ユニオン型、Mapped Types、インデックスアクセス型と、1 問目にしては多くの概念が登場しました。実際の業務において自前で複雑な型を実装する機会はさほど多くないかもしれませんが、書けたり読めたりして損はないと思うので、コツコツと頑張っていきたいものです。

Happy Coding! :)

採用情報

ご興味ある方はこちらから!

https://recruiting-tech-globis.wraptas.site/

脚注
  1. Pick<T, Keys> | TypeScript 入門『サバイバル TypeScript』 ↩︎

  2. 型引数の制約 | TypeScript 入門『サバイバル TypeScript』 ↩︎

  3. keyof 型演算子 | TypeScript 入門『サバイバル TypeScript』 ↩︎

  4. ユニオン型 (union type) | TypeScript 入門『サバイバル TypeScript』 ↩︎

  5. Mapped Types | TypeScript 入門『サバイバル TypeScript』 ↩︎

  6. インデックスアクセス型 (indexed access types) | TypeScript 入門『サバイバル TypeScript』 ↩︎

GLOBIS Tech

Discussion