打字稿中可选的必需参数

时间:2019-06-30 11:54:25

标签: typescript function vue.js redux vuex

我正试图强力键入一个通量实现(更具体地说,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 个答案:

答案 0 :(得分:3)

这是假设您需要编译器根据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引入了support for using tuples to represent function parameter lists。这为我们提供了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类型的参数和一个Tail<Parameters<Actions[A]>>类型的rest参数。

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

看起来不错。希望能有所帮助;祝你好运!

Link to code

相关问题