Опционально обязательные параметры в машинописи - PullRequest
2 голосов
/ 30 июня 2019

Я пытаюсь строго ввести реализацию потока (Vuex, чтобы быть более конкретным). Мой текущий код выглядит так:

const actions = {
  first(context: Context, payload: string) { return doSomething(context, payload); },
  second(context: Context) { return doSomethingWithNoPayload(context); }
}

type Actions = typeof actions;
type PayloadType<A extends keyof Actions> = Parameters<Actions[A]>[1];

function dispatch<A extends keyof Actions>(action: A): ReturnType<Actions[A]>;
function dispatch<A extends keyof Actions>(action: A, payload: Payload<A>): ReturnType<Actions[A]>;
function dispatch<A extends keyof Actions>(action: A, payload: Payload<A> = undefined): Promise<any> {
  return Promise.resolve({ action, payload });
}

Что я пытаюсь сделать, это:

dispatch("first") // error, no payload specified
dispatch("first", false) // error, payload type is not correct
dispatch("first", "correct") // ok, payload type is correct

dispatch("second", "something") // error, shouldn't pass payload`
dispatch("second") // ok, payload is not passed
dispatch("third") // error, non-existent action

Но использование необязательного параметра в качестве полезной нагрузки не заставляет меня передавать полезную нагрузку «первому» действию, которое действительно нуждается в полезной нагрузке. Если я не объявляю полезную нагрузку необязательной, я вынужден вызвать dispatch("second", undefined), потому что теперь мне нужно передать 2 параметра.

Есть мысли? Очень признателен.

1 Ответ

3 голосов
/ 30 июня 2019

Вот как я бы поступил, если бы вам понадобился компилятор для программного определения сигнатуры dispatch() типа actions.Во-первых, чтобы привести ваш пример к компиляции, я угадаю несколько типов, которые, надеюсь, не влияют на правильность решения:

// who knows
type Context = { c: string };
declare function doSomething(c: Context, payload: string): number;
declare function doSomethingWithNoPayload(c: Context): boolean;

const actions = {
  first(context: Context, payload: string) { return doSomething(context, payload); },
  second(context: Context) { return doSomethingWithNoPayload(context); }
}

type Actions = typeof actions;

В TypeScript 3.0 появилась поддержка для использования кортежей для представления функциисписки параметров .Это дает нам псевдоним типа Parameters<FuncType>, который возвращает кортеж, когда вы используете.Но это также дает нам больше возможностей для манипулирования кортежами в целом.Здесь мы определяем Tail<T>, который принимает тип кортежа T и возвращает новый кортеж с удаленным первым элементом:

// strip the first element off a tuple
// e.g., Tail<[1,2,3]> is [2,3]
type Tail<T extends readonly any[]> =
  ((...t: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never;

Затем мы можем сделать одну подпись для dispatch(), которая принимает один аргументтипа A и параметр rest типа Tail<Parameters<Actions[A]>>.

// use rest tuples
declare function dispatch<A extends keyof Actions>(
  action: A, ...payload: Tail<Parameters<Actions[A]>>
): ReturnType<Actions[A]>;

Это должно дать вам именно то поведение, которое вы хотите:

dispatch("first") // error, no payload specified
dispatch("first", false) // error, payload type is not correct
dispatch("first", "correct") // ok, payload type is correct

dispatch("second", "something") // error, shouldn't pass payload`
dispatch("second") // ok, payload is not passed
dispatch("third") // error, non-existent action

Хорошо выглядит.Надеюсь, это поможет;удачи!

Ссылка на код

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