Принимает кортеж смешанного типа в функции без потери проверки типа

#typescript

#typescript

Вопрос:

 import encodeParam from './encodeParam'

interface UrlParam<T> {
  name: string
  defaultValue: T
  encode: (value: T) => string | null
  decode: (value: string) => T
}

function buildQueryString<T>(...params: [UrlParam<T>, T][]) {
  const searchParams = new URLSearchParams()

  params.forEach(([param, value]) => {
    const newValue = encodeParam(param, value)

    if (newValue) {
      searchParams.set(param.name, newValue)
    }
  })

  return searchParams.toString()
}

const one: UrlParam<string> = {
  name: 'one',
  defaultValue: '',
  encode: (val) => val,
  decode: (val) => val,
}

const two: UrlParam<number> = {
  name: 'two',
  defaultValue: 0,
  encode: (val) => val.toString(),
  decode: (val) => Number(val),
}

const query = buildQueryString(
  [one, 'foo'],
  [two, 42], // This will not compile because only UrlParam<string> is expected.
)
 

Я написал функцию, вызываемую buildQueryString для кодирования ряда параметров запроса с использованием кортежей, которые содержат определение параметра ( UrlParam ) и значение, которое следует использовать. Я изо всех сил пытаюсь настроить сигнатуру метода для принятия смешанного типа UrlParam<T> , как это можно сделать?

Это, конечно, должно работать так, чтобы [UrlParam<T>, T] ограничение кортежа по-прежнему проверялось, поэтому any типы не используются.

Ответ №1:

Ссылка на игровую площадку

Ключ к набору функций, подобных этой, заключается в том, чтобы убедиться, что общий тип может содержать всю необходимую информацию. С помощью всего лишь a T все преобразуется в объединения, и мы теряем порядок. Вместо этого мы можем использовать общий тип массива / кортежа (который я назвал A ), где каждая запись соответствует каждому кортежу аргументов:

 type UrlParamPair<T> = [ UrlParam<T>, T ];
type UrlParamPairs<A extends any[]> = { [K in keyof A]: UrlParamPair<A[K]> };

function buildQueryString<A extends any[]>(...params: UrlParamPairs<A>) {
    ...
}
 

UrlParamPairs Сопоставленный тип (массив) преобразует массив типов в массив кортежей, например UrlParamPairs<[string, number]> , становится [ UrlParamPair<string>, UrlParamPair<number> ]

Вывод типа для вызова функции показывает, что это работает:

 function buildQueryString<[string, number]>(
    params_0: UrlParamPair<string>, params_1: UrlParamPair<number>): string
 

Комментарии:

1. Потрясающе, это именно то, что мне было нужно. Теперь, чтобы выяснить, как я могу сделать это с помощью массива вместо расширенного аргумента.