У меня есть ниже машинопись кода ( ссылка на" rel = "nofollow">

type MyCallback<T> = (s: string, payload: T) => void;

interface IActions {
  do1: MyCallback<number>;
  do2: MyCallback<string>;
  [key: string]: (s: string, payload: any) => void;
}

function convert<T extends { [key: string]: (s: string, payload: any) => void }>(callbackMap: T) {

  const result: { [key: string]: <U>(payload: U) => void } = {};

  Object.keys(callbackMap).forEach(key => {
    if (typeof callbackMap[key] === 'function') {
      result[key] = callbackMap[key].bind(null, "data");
    }
  })

  return result;
}

const maps = convert<IActions>({
  do1: (s: string, payload: number) => {
    //
  },
  do2(s: string, payload: string) {
    //
  }
});

maps.do1(1); // valid
maps.smth("1"); // should be type-check error, but TS thinks it's valid

Я пытаюсь создать функцию, которая принимает объект через интерфейс. Функция преобразует все методы из объекта в новый объект, где все методы имеют один фиксированный параметр (через метод bind). Другими словами, я хочу преобразовать этот интерфейс

interface IActions {
  do1: (state: string, payload: number);
  do2: (state: string, payload: string);
  .....
}

К

interface IActions {
  do1: (payload: number);
  do2: (payload: string);
  ....
}

Я хочу сделать его универсальным, чтобы он преобразовывал любой интерфейс на основе универсального параметра.

Проблема с моим текущим подходом заключается в том, что у меня нет никакого intellisense и проверки типов для моего объекта maps.

Можно ли изменить мою функцию convert таким образом, чтобы этот тип возвращаемого значения автоматически определялся входящим интерфейсом? Другими словами, у меня есть полная проверка типов и intellisense для возвращаемого значения (maps в моем случае).

3
Kai 24 Окт 2019 в 10:54

1 ответ

Лучший ответ

Тот факт, что maps.smth действителен, связан с явной подписью индекса для результата. Здесь вам нужен отображаемый тип для сопоставления свойств IActions новому типу, содержащему измененные методы. Чтобы создать новую сигнатуру метода, мы можем использовать условный тип для извлечения остальных параметров (пропустить первый)

type MyCallback<T> = (s: string, payload: T) => void;

interface IActions {
  do1: MyCallback<number>;
  do2: MyCallback<string>;
}

function convert<T extends Record<keyof T, (s: string, payload: any) => void>>(callbackMap: T) {

  const result: Record<string, (...a: any[]) => any> = {}

  Object.keys(callbackMap).forEach(key => {
    if (typeof callbackMap[key as keyof T] === 'function') {
      result[key] = callbackMap[key as keyof T].bind(null, "data");
    }
  })

  return result as {
    [P in keyof T]: T[P] extends (s: string, ...p: infer P) => infer R ? (...p: P) => R : never;
  };
}

const maps = convert<IActions>({
  do1: (s: string, payload: number) => {
    //
  },
  do2(s: string, payload: string) {
    //
  }
});

maps.do1(1); // valid
maps.do1("1"); //err
maps.smth("1"); // should be type-check error, but TS thinks it's valid

Играть

1
Titian Cernicova-Dragomir 24 Окт 2019 в 08:32