項目28 クラスやカリー化を使って型パラメーターを段階的に割り当てる
中途半端なパラメータ指定はできない
TyepScirptの型推論は全か無のいずれかである
型パラメータのすべてを使用法からTyepScirptに推論させるか、明示的に指定するかのどちらかで、その中間はない
code:ts
export interface SeedAPI {
'/seeds': Seed[];
'/seed/apple': Seed;
'/seed/strawberry': Seed;
}
declare function fetchAPI<
API, Path extends keyof API
(path: Path): Promise<APIPath>; fetchAPI<SeedAPI>('/seed/strawberry');
// ~~~~~~~ Expected 2 type arguments, but got 1.
上記のエラーを解決したコード
code:ts
const berry = fetchAPI<SeedAPI, '/seed/strawberry'>('/seed/strawberry'); // OK
// ^? const berry: Promise<Seed>
解決はできたが、重複が生まれている
クラスとカリー化を用いて、型パラメータAPIを明示的に指定する場所と、型パラメータPathを推論させる場所を分ける クラス
クラスは状態を保持するのに適しており、関連する一覧の関数に同じ状態を繰り返し渡す手間を省くことができる
先程の問題を解決するためのクラス
クラスにジェネリック型としてAPIを指定できるようすることで、fetchメソッド利用時のpathの値がAPI内に定義されているかを型チェックできるようにしている
code:ts
declare class ApiFetcher<API> {
fetch<Path extends keyof API>(path: Path): Promise<APIPath>; }
code:ts
const fetcher = new ApiFetcher<SeedAPI>();
const berry = await fetcher.fetch('/seed/strawberry'); // OK
fetcher.fetch('/seed/chicken');
// ~~~~~~~~~~~~~~~
// Argument of type '"/seed/chicken"' is not assignable to type 'keyof SeedAPI'
const seed: Seed = await fetcher.fetch('/seeds');
// ~~~~ Seed[] is not assignable to Seed
カリー化
カリー化は、型パラメータを何段階にも分けて割り当てることを可能にする柔軟性をもたらす。 先ほどの問題をカリー化を使って解決する関数
code:ts
declare function fetchAPI<API>():
<Path extends keyof API>(path: Path) => Promise<APIPath>; code:ts
const berry = await fetchAPI<SeedAPI>()('/seed/strawberry'); // OK
fetchAPI<SeedAPI>()('/seed/chicken');
// ~~~~~~~~~~~~~~~
// Argument of type '"/seed/chicken"' is not assignable to type 'keyof SeedAPI'
const seed: Seed = await fetchAPI<SeedAPI>()('/seeds');
// ~~~~ Seed[] is not assignable to Seed
TypeScriptでカリー化を使う場合の利点
カリー化を使うと、ローカルの型エイリアスを定義可能なスコープが作られる
code:ts
function fetchAPI<API>() {
type Routes = keyof API & string; // ローカルの型エイリアス
return <Path extends Routes>(
path: Path
): Promise<APIPath> => fetch(path).then(r => r.json()); }