#typescript
Вопрос:
TL;DR — Как мне удалить ошибку в этой ссылке на игровую площадку, не прибегая к @ts-expect-error
ней ?
Представьте, что мы определяем удаленный API…
const api = {
str2num(_arg: string) { return 42; },
num2str(_arg: number) { return 'forty-two'; },
};
type API = typeof api;
type APIFuncName = keyof API;
type Func<FN extends APIFuncName> = API[FN];
export function exec<FN extends APIFuncName>(
name: FN,
...args: Parameters<Func<FN>>
) {
// Unexpected error:
// "A spread argument must either have a tuple type or be passed to a rest parameter."
return api[name](...args);
}
// This is fine
console.log(exec('str2num', 'zero'));
console.log(exec('num2str', 0));
// An error as expected: Type 'number' is not assignable to type 'string'
// console.log(exec('str2num', 0));
Мы можем использовать эту exec(...)
функцию просто отлично, однако TypeScript неожиданно жалуется на реализацию exec(...)
.
Я думаю, проблема в том, что TypeScript не может связать тип args
с параметрами api[name]
.
Сообщение об ошибке несколько сбивает с толку («Аргумент spread должен либо иметь тип кортежа, либо быть передан параметру rest.»), поскольку args
явно уже имеет тип кортежа.
Как я могу ввести это правильно, не прибегая к @ts-expect-error?
Ответ №1:
Я нашел решение этой проблемы, но это немного похоже на взлом; в принципе, вы можете написать вспомогательную функцию для выполнения фактического вызова функции, где функция вводится для принятия аргументов в качестве параметра rest. Это удовлетворяет части сообщения об ошибке «… или будет передано параметру rest». Я не уверен, есть ли элегантное решение.
export function exec<FN extends APIFuncName>(
name: FN,
...args: Parameters<Func<FN>>
) {
return callHelper(api[name], args);
}
function callHelper<F extends (...args: any[]) => any>(
f: F,
args: Parameters<F>
): ReturnType<F> {
return f(...args);
}
Комментарии:
1. Спасибо! Это менее грязно, чем мое лучшее обходное решение, которое заключалось в
switch (name)
повторении одного и того жеapi[name](...args)
кода в каждом блоке case. По причинам, которые я не понимаю, это удовлетворяет другую сторону сообщения об ошибке «… Аргумент распространения должен либо иметь тип кортежа». Я бы хотел увидеть исправление, которое не включало бы изменение JavaScript2. Я подозреваю, что это удовлетворяет части «должен быть тип кортежа», потому что в каждом случае оператора switch у вас есть тип кортежа, а не (эффективно) объединение типов кортежей. Единственное исправление, которое я смог найти, которое не изменило Javascript, — это использование утверждения типа, которое вы можете счесть достаточно безопасным для такой простой функции, как эта.
3. Если у вас есть время — я бы хотел посмотреть, как это исправит утверждение типа. Я не мог его найти.
4. О, конечно, самый простой из них таков
(api[name] as Function)(...args)
.5. Ах, я неправильно понял, я думал, что у вас есть способ использовать утверждения типа, чтобы соединить 2 части. Тем не менее, это лучше, чем @ts-ожидать-ошибки. Спасибо за помощь!