Вот как я бы поступил, если бы вам понадобился компилятор для программного определения сигнатуры 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
Хорошо выглядит.Надеюсь, это поможет;удачи!
Ссылка на код