Типы для шаблона посредника в TypeScript

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

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

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

Любые идеи?


person Jose A    schedule 07.04.2020    source источник


Ответы (1)


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

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

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

См. ниже:

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

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>;

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

person Jose A    schedule 21.04.2020