#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. Потрясающе, это именно то, что мне было нужно. Теперь, чтобы выяснить, как я могу сделать это с помощью массива вместо расширенного аргумента.