Skip to content

Latest commit

 

History

History
126 lines (95 loc) · 7.2 KB

conditional-varargs.md

File metadata and controls

126 lines (95 loc) · 7.2 KB

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

Things to Remember

  • 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.

Code Samples

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

💻 playground


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


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

💻 playground


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

💻 playground


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


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


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


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


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

💻 playground