Как связать входные аргументы функции с типом возвращаемого значения, чтобы использовать функции автозаполнения - PullRequest
0 голосов
/ 26 февраля 2019

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

В принципе, у меня есть следующая функция:

function postCommand<T extends AvailableCommands>(
    commandName: T,
    commandArguments: ArgumentType<T>
): Observable<PayloadType<T>> {
    return httpClient.post<PayloadType<T>>(`/command/${commandName}`, commandArguments);
}

в сочетании сследующие (примерные) определения:

type AvailableCommands =
    | 'create-group'
    | 'create-post';


type ArgumentType<T extends AvailableCommands> = T extends 'create-group'
    ? CreateGroupArguments
    : T extends 'create-post'
    ? CreatePostArguments
    : never;

type PayloadType<T extends AvailableCommands> = T extends 'create-group'
    ? CreateGroupPayload
    : T extends 'create-post'
    ? CreatePostPayload
    : never;

Примечание: *Arguments и *Payload определены вне области действия, а также в виде простых интерфейсов для объектов.Например, с ключами groupId и body для CreatePostArguments.httpClient также определен вне области действия (в основном это HTTP-клиент Angular7)

Приведенный выше код позволяет мне использовать функцию postCommand с автопредставлениями для входной переменной commandName и автопредставлениями для commandArguments на основе которого commandName я набрал. commandPayload также выводится, когда я подписываюсь на возвращаемую наблюдаемую информацию.

Теперь использование условных типов уродливо и не так легко понимается другими разработчиками.

Это привело меня к попытке найти другой способ, где я наткнулся на [

. Это изменяет код следующим образом:

function postCommand<T extends AvailableCommands>(
    commandName: T,
    commandArguments: CommandMap[T]['arguments']
): Observable<CommandMap[T]['payload']> {
    return httpClient.post<CommandMap[T]['payload']>(`/command/${commandName}`, commandArguments);
}

interface CommandMap {
    'create-group': { arguments: CreateGroupArguments; payload: CreateGroupPayload };
    'create-post': { arguments: CreatePostArguments; payload: CreatePostPayload };
}

Теперь, это уже выглядит более читабельным и имееттот же эффект.У меня все еще есть самовнушения.Единственное предостережение: если я опускаю create-post в интерфейсе CommandMap, компилятор не возражает.Это проблема обеих вышеуказанных реализаций.

Это в основном то, что я хотел, за исключением оговорки, которую я упомянул.Итак, мой вопрос сейчас таков:

Есть ли способ заставить компилятор учесть тот факт, что я не определил отображения для всех AvailableCommands в CommandMap, и при этом все еще имею автопредставление для аргументов и возврататип postCommand?

(я создал Playground с моей последней версией, и имитирующую реализацию post и версию TypeScript, которую я использую для своего проекта)

Редактировать:

Общий лучший ответ для меня был скрыт на виду:

Сейчас я в основном использую только один тип

export interface CommandMap {
    [index: string]: { arguments: any; payload: any };
    'create-group': { arguments: CreateGroupArguments; payload: CreateGroupPayload };
    'create-post': { arguments: CreatePostArguments; payload: CreatePostPayload };
}

и вызов функции:

public postCommand<T extends keyof CommandMap>(
    commandName: T,
    commandArguments: CommandMap[T]['arguments']
): Observable<CommandMap[T]['payload']> {
    return this.httpClient.post<CommandMap[T]['payload']>(`/command/${commandName}`, commandArguments);
}

Общий теперь расширяет keyof CommandMap.Так как CommandMap уже имеет все определения для моих команд, мне не потребовался дополнительный тип AvailableCommands.

Все функции AutoSuggestion по-прежнему доступны, и ошибки находятся в нужном месте (интерфейссамо определение, если оно вообще есть)

1 Ответ

0 голосов
/ 26 февраля 2019

Если вы ищете ошибку компилятора, если ключи CommandMap не совпадают с AvailableCommands, вы можете сделать это в отдельной строке:

type VerifyCommandMap<
  // if the next line is an error, CommandMap has extra keys
  K extends AvailableCommands = keyof CommandMap,
  // if the next line is an error, CommandMap is missing some keys
  L extends keyof CommandMap = AvailableCommands 
  > = true;

В основном вы 'принуждение параметров по умолчанию для удовлетворения общих ограничений, которые они удовлетворяют, только если AvailableCommands совпадает с keyof CommandMap.

Это помогает?

...