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 }
// ...
}
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('/'))
ts
buildURL('/', { query: 'recursion' }) // should be an error (no params for root)
buildURL('/search') // should be an error (missing params)
ts
function buildURL<Path extends keyof RouteQueryParams>(
route: Path,
params: RouteQueryParams[Path]
) {
return route + (params ? `?${new URLSearchParams(params)}` : '')
}
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 '{}'
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.
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)}` : '')
}
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
ts
buildURL('/')
// ^? function buildURL<"/">(route: "/"): string
buildURL('/search', { query: 'do a barrel roll' })
// ^? function buildURL<"/search">(
// route: "/search", params: { query: string; language?: string; }
// ): string