#javascript #reactjs #typescript
#javascript #reactjs #typescript
Вопрос:
У меня есть функция с определенными типами аргументов. Аргументы key
, которые являются ключом указанного интерфейса IBook
, и value
которые должны иметь тип, соответствующий этому конкретному ключу в интерфейсе. Т.е. если key === 'id'
тогда единственным приемлемым типом для value
должно быть number
.
Проблема возникает, когда я хочу создать другую функцию, чтобы просто передавать аргументы из onChange
события в первое. Чтобы избежать повторного объявления аргументов функции снова, я использовал Parameters
generic, но, похоже, он ведет себя неправильно. Проверьте использование ниже.
interface IBook {
id: number;
title: string;
isPromoted?: boolean;
}
export const editBook = <K extends keyof Required<IBook>>(
key: K,
value: Required<IBook>[K],
) => ({
type: 'EDIT_BOOK',
payload: { key, value },
});
const onChange = (...args: Parameters<typeof editBook>) => {
dispatch(editBook(...args));
};
editBook('id', 'some string'); // string not accepted here, shows error
onChange('id', 'some string'); // no error here
editBook('id', true); // boolean not accepted here, shows error
onChange('id', true); // no error here
Если я использую исходную функцию editBook
, то value
она вводится правильно — это только та, которая соответствует типу key
. Если я использую другой, отображается ошибка. Однако, если я использую функцию-оболочку onChange
, то в качестве аргумента принимается любой тип, существующий в IBook
value
.
Как я могу это исправить?
Ответ №1:
Когда вы используете Parameters
, вы не фиксируете параметры типа исходной функции, typescript будет просто использовать ограничение, где он находит любую ссылку на параметр типа, поэтому подпись onChange
на самом деле будет просто:
(key: keyof IBook, value: Required<IBook>[keyof IBook]) => void
что сработает для:
(key: "id" | "title" | "isPromoted", value: string | number | boolean) => void
разрешение недопустимых вызовов.
К сожалению, нет явного способа захватить параметры типа и перенаправить их в новую функцию. Начиная с версии 3.4, существует неявный способ сделать это, используя функцию канала, как описано здесь
function pipe<A extends any[], B, C>(ab: (...args: A) => B, bc: (b: B) => C): (...args: A) => C {
return (...args: A) => bc(ab(...args));
}
function dispatch<T extends { type: string }>(action: T): void {
}
const onChange = pipe(editBook, dispatch); // generic type parameter preserved
editBook('id', 'some string'); // string not accepted here, shows error
onChange('id', 'some string'); // error now
editBook('id', true); // boolean not accepted here, shows error
onChange('id', true); // error now
Комментарии:
1. Спасибо за объяснение! Какой позор
Parameters
, что так не работает.