TL;DR

  • TypeScript では、型パラメータをすべて推論させるか、すべて明示するかのどちらかしか選べない。
  • 一部だけを明示したいときは、クラスやカリー化で型パラメータを段階的に割り当てる。

内容

型パラメータは一部だけ明示できない

TypeScript では、型パラメータをすべて推論させるか、すべて明示的に指定するかを選ぶ必要がある。そのため、次のコードは型エラーになる。

function fetchAPI<API, Path extends keyof API>(path: Path): Promise<API[Path]> { ... }
 
export interface SeedAPI {
  '/seed': Seed[]
  '/seed/apple': Seed
  '/seed/strawberry': Seed
}
fetchAPI<SeedAPI>('/seed/strawberry') // 型エラー: Expected 2 type arguments, but got 1.

Path の型も渡せば解消するが、記述が冗長になる。

fetchAPI<SeedAPI, "/seed/strawberry">("/seed/strawberry")

型パラメータを段階的に割り当てる

これを避けるには、型パラメータを段階的に渡す。1 つ目の方法はクラスを使うことである。

declare class ApiFetcher<API> {
  fetch<Path extends keyof API>(path: Path): Promise<API[Path]>
}
const fetcher = new ApiFetcher<SeedAPI>() // 明示的に指定
const berry = await fetcher.fetch("/seed/strawberry") // 型推論

2 つ目の方法はカリー化である。

declare function fetchAPI<API>(): <Path extends keyof API>(path: Path) => Promise<API[Path]>
const berry = await fetchAPI<SeedAPI>()("/seed/strawberry")

どちらを採用してもよいが、カリー化ではローカルな型エイリアスを作れる。

function fetchAPI<API>() {
  // ローカルな型エイリアス
  type Routes = keyof API & string
 
  return <Path extends Routes>(path: Path): Promise<API[Path]> => fetch(path).then((r) => r.json())
}

キーワード