TypeScript: почему общие параметры принимают неправильные аргументы?

#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 , что так не работает.