mosya<TC> - チェイナブルなオブジェクトに型をつけようの解説
この記事はmosya<TC>の問題の一つであるChainable型の解説になります。
問題
JavaScript では、チェイン可能なオプションがよく使われます。しかし、TypeScript に切り替えたとき、正しく型を付けることができますか?
この課題では、オブジェクトでもクラスでも何でもいいので、 option(key, value) と get() の 2 つの関数を提供する型を定義してください。option では、与えられたキーと値を使って現在の config の型を拡張できます。最終的な結果は get で取得することにしましょう。
例えば
declare const config: Chainable;
const result = config
.option("foo", 123)
.option("name", "type-challenges")
.option("bar", {
value: "Hello World",
})
.get();
// expect the type of result to be:
interface Result {
foo: number;
name: string;
bar: {
value: string;
};
}
この問題を解くために js/ts のロジックを書く必要はありません。型レベルのロジックだけを書いてください。
key は string のみを受け付け、value は任意の型を受け付けると仮定しても構いません。同じ key が 2 回渡されることはありません。
解答例
type Chainable<T = {}> = {
option: <K extends string, V>(
key: K extends keyof T ? never : K,
value: V
) => Chainable<
Omit<T, K> & Record<K, V>
>;
get: () => T;
};
Chainable型は、optionとgetの2つのプロパティを持つオブジェクト型です。
まずはそれを前提に、optionとgetの型を定義していきます。
Tには初期値として空のオブジェクト型を指定します。
type Chainable<T = {}> = {
option: ...
get: () => ...
}optionは、keyとvalueを引数に取り、Chainable型を返す関数です。
keyはstring型のみを受け付けるので、K extends stringという条件を指定します。
valueは任意の型を受け付けるので、Vという型引数を指定します。
type Chainable<T = {}> = {
option: <K extends string, V>(key: K, value: V) => ...
get: () => ...
}ここで、optionメソッドで同じキーに対して値が2回渡されることを防ぐために、以下のようにkeyに対して制限します。
type Chainable<T = {}> = {
option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => ...
get: () => ...
}このようにkeyがすでにTのキーに含まれている場合は、neverを返すようにして、keyが重複しないように保証します。
次に、optionメソッドの返り値の型を定義します。
type Chainable<T = {}> = {
option: <K extends string, V>(key: K extends keyof T ? never : K, value: V) => Chainable<T & Record<K, V>>
get: () => ...
}Record<K, V>は、KとVを受け取り、Kをキー、Vを値とするオブジェクト型を返す型です。
オブジェクトTに今回optionメソッドで渡されたkeyとvalueを追加した新しい型を返すようにします。
最後に、getメソッドの返り値の型を定義します。
type Chainable<T = {}> = {
option: <K extends string, V>(
key: K extends keyof T ? never : K,
value: V
) => Chainable<T & Record<K, V>>;
get: () => T;
};
getメソッドは、Tを返せばいいので、get: () => Tという型を指定します。
Authored by

Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。
Related









