Типы для шаблона посредника в TypeScript - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь создать набранный шаблон посредника в TypeScript. Сам шаблон работает без проблем, но я бы хотел, чтобы он был полностью напечатан без избыточности (не определяя конкретный тип c для регистрации команды и диспетчера отдельно).

Это означает, что всякий раз, когда я делаю:

dispatchCommand({
type: "SAVE_EVIDENCE_EVALUATION",
command: {
 // This gets autocompleted with the right typing
}

Вот что мне удалось сделать до сих пор:

Нетипизированный код посредника

let commands = {};
    export const registerCommand = ({ type, handler }) => {
      commands[type] = handler;
    };

    export const dispatchCommand= async ({ type, command }) => {
      logTime(type);
      return commands[type](command);
    };

Я бы зарегистрировал команду, как это:

registerCommand("CREATE_NEW_INSTITUTION_ACCOUNT", CreateNewInstitutionAccountCommandHandler);

И я бы запустил ее, как это:

const result = await dispatchCommand("CREATE_NEW_INSTITUTION_ACCOUNT", { 
  tenantId: 'my-id',
  employeeId: 'the-other-id'
});

Я пытался в течение нескольких часов, как создать общий тип, поэтому я могу безопасно зарегистрировать команду с уже доступным типом.

Примечание: я всегда мог сделать 2 типа, но это несколько отразило бы назначение системы типов, так как я постоянно забываю добавлять ее в нескольких местах

Вот такой подход (TypeScript) :

type MediatorDispatcherForCommands = (({
  type,
  handler,
  command,
}: {
  type: "SAVE_EVIDENCE_EVALUATION";
  handler?: typeof SaveEvidenceEvaluationCommandHandler;
  command?: SaveEvidenceEvaluationCommand;
}) => ReturnType<typeof SaveEvidenceEvaluationCommandHandler>) &
  (({
    type,
    handler,
    command,
  }: {
    type: "SEED_DATABASE_INDIVIDUAL";
    handler?: typeof SaveEvidenceEvaluationCommandHandler;
    command?: SeedDataForIndividualAccountCommand;
  }) => ReturnType<typeof SeedDataForIndividualAccountCommandHandler>);

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

Это работает для диспетчера, но не для команды.

   export const dispatchCommand: MediatorDispatcherForCommands = async ({ type, command }) => {
      logTime(type);
      return commands[type](command);
    };

Есть идеи?

1 Ответ

0 голосов
/ 21 апреля 2020

Я прекратил отказываться от универсального решения ... Это было самое близкое, что я мог получить:

Я бы сделал операцию объединения в параметре аргумента. Это позволило бы мне автозаполнение при необходимости.

Позже, в диспетчере, я бы создал тип возврата, который я бы явно передал.

См. Ниже:

Это позволяет мне автозаполнять это как в диспетчере, так и в регистраторе.

export type MediatorCommands =
  | {
      type: "CREATE_ROLE";
      arg: CreatePermissionCommand | typeof createPermissionCommandHandler;
    }
  | {
      type: "CREATE_NEW_INSTITUTION_ACCOUNT";
      arg:
        | CreateNewInstitutionAccountCommand
        | typeof CreateNewInstitutionAccountCommandHandler;
    }

Тогда мои функции:

// This is hacky... but it works!
let commands: {
  [x: string]: Function;
} = {};

export function registerCommand({ type, arg }: MediatorCommands) {
  commands[type] = arg as Function;
}

export async function dispatchCommand<TResult>({
  type,
  arg,
}: MediatorCommands) {
  logTime(type);
  return commands[type](arg) as Promise<TResult>;
}

Затем, чтобы зарегистрировать команду:

registerCommand({
  type: "CREATE_ROLE",
  arg: createPermissionCommandHandler,
});

Затем вы можете сделать что-то вроде этого:

await dispatch<CreatedRoleRT>({
          type: "CREATE_ROLE",
          arg: {
            tenantId,
            employeeId,
          },
        });

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

type CreatedRoleRT = ReturnType<typeof createPermissionCommandHandler>;

Это не идеально ... но работает ?!

...