Как исправить универсальную функцию TypeScript, чтобы она возвращала действительный выведенный тип? - PullRequest
1 голос
/ 24 октября 2019

Ниже приведен код TypeScript ( ссылка на игровую площадку ):

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);
  ....
}

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

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

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

1 Ответ

1 голос
/ 24 октября 2019

То, что 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

Play

...