TL;DR

  • レストパラメータとタプル型を使うと、シグネチャが引数の型に依存する関数をモデリングできる。
  • 条件型を使うと、あるパラメータの型と他のパラメータの型の関係を表現できる。タプルの要素にはラベルを付ける。

内容

レストパラメータとタプル型

レストパラメータとタプル型を使うと、シグネチャが引数の型に依存する関数をモデリングできる。さらに条件型を使うと、あるパラメータの型と、それ以外のパラメータの型の関係をモデリングできる。次の buildURL は、ルートによってクエリパラメータが要るかどうかが変わる。

function buildURL<Path extends keyof RouteQueryParams>(
  route: Path,
  ...args: RouteQueryParams[Path] extends null ? [] : [params: RouteQueryParams[Path]]
) {
  const params = args[0]
  return route + (params ? `?${new URLSearchParams(params)}` : "")
}

呼び出し側で分かりやすいパラメータ名を表示するため、タプルの要素にはラベルを付ける。付けないと、次のように args_0 のような名前で表示される。

function buildURL<"/search">(
  route: "/search",
  args_0: {
    query: string
    language?: string
  },
): string

訳者による解説

特定の文字列と型が結びつくのは、JavaScript でよく見るパターンである。たとえば DOM のイベントハンドラでは、イベント名とイベントオブジェクトの型が結びついている(click と MouseEvent)。

この関係を次のように interface で定義する方法は、直感的で分かりやすいので広く使える。

interface RouteQueryParams {
  "/": null
  "/search": { query: string; language?: string }
}
// OK
buildURL("/")
buildURL("/search", { query: "foo" })
buildURL("/search", { query: "bar", language: "en" })
 
// 型エラー
buildURL("/", { query: "recursion" })
buildURL("/search", {})

キーワード