Skip to content

Item 62: Use Rest Parameters and Tuple Types to Model Variadic Functions

要点

  • Use rest parameters and tuple types to model functions whose signature depends on the type of an argument.
  • Use conditional types to model relationships between the type of one parameter and the number and type of the remaining parameters.
  • Remember to label the elements of your tuple types to get meaningful parameter names at call sites.
  • 使用剩余参数和元组类型来建模函数签名,这些签名依赖于某个参数的类型。
  • 使用条件类型来建模一个参数的类型与其余参数的数量和类型之间的关系。
  • 记得标记元组类型的元素,以便在调用时获得有意义的参数名称。

正文

ts
interface RouteQueryParams {
  '/': null
  '/search': { query: string; language?: string }
  // ...
}

💻 playground


ts
function buildURL(route: keyof RouteQueryParams, params?: any) {
  return route + (params ? `?${new URLSearchParams(params)}` : '')
}

console.log(buildURL('/search', { query: 'do a barrel roll', language: 'en' }))
console.log(buildURL('/'))

💻 playground


ts
buildURL('/', { query: 'recursion' }) // should be an error (no params for root)
buildURL('/search') // should be an error (missing params)

💻 playground


ts
function buildURL<Path extends keyof RouteQueryParams>(
  route: Path,
  params: RouteQueryParams[Path]
) {
  return route + (params ? `?${new URLSearchParams(params)}` : '')
}

💻 playground


ts
buildURL('/search', { query: 'do a barrel roll' })
buildURL('/search', { query: 'do a barrel roll', language: 'en' })
buildURL('/search', {})
//                  ~~ Property 'query' is missing in type '{}'

💻 playground


ts
buildURL('/', { query: 'recursion' }) // error, good!
//            ~~~~~~~~~~~~~~~~~~~~ Argument of type '{ query: string; }' is
//                                 not assignable to parameter of type 'null'
buildURL('/', null) // ok
buildURL('/') // we'd like this to be allowed
// ~~~~~ Expected 2 arguments, but got 1.

💻 playground


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

💻 playground


ts
buildURL('/search', { query: 'do a barrel roll' })
buildURL('/search', { query: 'do a barrel roll', language: 'en' })
buildURL('/search', {})
//                  ~~ Property 'query' is missing in type '{}' ...

buildURL('/', { query: 'recursion' })
//            ~~~~~~~~~~~~~~~~~~~~ Expected 1 arguments, but got 2.
buildURL('/', null)
//            ~~~~ Expected 1 arguments, but got 2.
buildURL('/') // ok

💻 playground


ts
buildURL('/')
// ^? function buildURL<"/">(route: "/"): string
buildURL('/search', { query: 'do a barrel roll' })
// ^? function buildURL<"/search">(
//      route: "/search", params: { query: string; language?: string; }
//    ): string

💻 playground

Released under the MIT License.