#typescript
#typescript
Вопрос:
Я пытаюсь вернуть из функции кортеж точно такой же длины, что и кортеж, переданный в качестве параметра.Я думал, что мне удалось сделать это с помощью use generics, но я все еще сталкиваюсь с ошибкой при использовании spread operator для результата.
То, что я пытаюсь сделать, лучше всего объяснить на примере. Я пытаюсь написать некоторую вспомогательную функцию для проверки и извлечения параметров запроса из объекта экспресс-запроса.
Первый пример без типов —
const getQueryParams = (req: Request) => (keys) => {
const values = keys.map(key => {
const value = req.query[key];
if (value !== undefined amp;amp; value !== null amp;amp; typeof value === 'string') {
return value;
}
throw new Error(`Missing query param ${key}`);
});
return values;
}
Я добавил несколько вспомогательных типов, чтобы попытаться сказать — то, что возвращает эта функция, имеет точно такую же длину, какую она передала:
type ArrLength<T extends readonly any[]> = T['length'];
type SameLength<T extends readonly any[]> = readonly string[] amp; {length: ArrLength<T>};
const getQueryParams = (req: Request) => <T extends readonly string[]>(keys: T): SameLength<T> => {
const values = keys.map(key => {
const value = req.query[key];
if (value !== undefined amp;amp; value !== null amp;amp; typeof value === 'string') {
return value;
}
throw new Error(`Missing query param ${key}`);
}) as SameLength<T>;
return values;
}
и, похоже, это работает, по крайней мере частично. Например, это работает:
let params = getQueryParams(req)(['token', 'user', 'count'] as const);
params = ['1'] as const; //ERROR! notice typescript knows here that parmas is of length 3
params = ['1', '2', '3'] as const; //OK! notice typescript knows here that parmas is of length 3
однако попытка вызвать функцию с помощью оператора распространения:
someApiMethod(...params)
Я получаю сообщение об ошибке, Expected 3 arguments, but got 0 or more
.
Полная ссылка на игровую площадку
У кого-нибудь есть идеи, как я исправляю свои типы / делаю какой-то другой обходной путь?
(В качестве альтернативы, если это неразрешимо, я бы с радостью принял ответ, объясняющий, почему, и соответствующую проблему в репозитории github Typescript, если она существует. Я не могу найти ни одного)
Ответ №1:
Я думаю, вам будет проще, если вы будете использовать сопоставленные типы кортежей, подобные этому:
type SameLength<T extends readonly any[]> = { [K in keyof T]: string };
Это приводит к получению подлинного типа кортежа, когда вы передаете его в:
type TestTuple = SameLength<[false, 1, "two", Date]>;
// type TestTuple = [string, string, string, string]
Обратите внимание, что вам может потребоваться промежуточное утверждение для получения результата map()
from string[]
to SameLength<T>
, например:
const getQueryParams = (req: Request) => <T extends readonly string[]>(keys: T): SameLength<T> => {
const values = keys.map(key => {
const value = req.query[key];
if (value !== undefined amp;amp; value !== null amp;amp; typeof value === 'string') {
return value;
}
throw new Error(`Missing query param ${key}`);
}) as readonly string[] as SameLength<T>; // might require intermediate assertion
return values;
}
И теперь это params
подлинный кортеж, вы сможете его распространять:
const handler = async (req: Request, res: any) => {
let params = getQueryParams(req)(['token', 'user', 'count'] as const);
const r = await someApiMethod(...params); // okay
res.json(r);
}