推断打字稿函数参数

Infer typescript function arguments

鉴于以下情况

const action1 = (arg1: string) => {}
const action2 = (arg1: string, arg2: {a: string, b: number}) => {}
const actions = [action1, action2]
handleActions(actions)

... elsewhere ...

const handleActions = (actions: WhatTypeIsThis[]) => {
  const [action1, action2] = actions;
  action1(/** infer string */)
  action2(/** infer string and object */)
}

如何定义 WhatTypeIsThis 类型,以便在 handleActions 中推断操作参数?

是否可以将其定义为 actions 可以是具有不同参数列表的任意数量的函数?

是否可以使用泛型?

我的解决方案:

我标记了已接受的答案,因为它是我解决方案的灵感来源。

// get the types of the actions, defined in outer scope
type GET = typeof api.get;
type CREATE = typeof api.create;
...

// in controller
handleActions([api.getSomething, api.create])

...

// in service handler
const handleActions = (actions: [GET, CREATE]) => {
  const [action1, action2] = actions;
  // now we have all input / output type hints
}

这种方法让我可以将我的逻辑与 http 服务器、auth 以及我没有编写的所有其他内容隔离开来,这样我就可以安心地测试我的服务处理程序中的复杂性。

Is it possible to define it in such a way that actions can be any number of functions with varying argument lists?

对于动态列表,您需要运行时检查。我不认为你可以在不标记它们的情况下对这些函数进行运行时检查(我尝试在 length 上进行,因为这两个函数的长度不同,但它没有用,而且它真的没有用即使它有 — (arg1: string) => void(arg1: number) => void 是具有相同 length) 的不同函数。

通过品牌和运行时检查,有可能:

  • 为函数定义品牌类型
  • 创建函数
  • 将动作列表定义为函数类型的联合数组
  • 在该品牌上有 handleActions 个分店

像这样:

type Action1 = (
    (arg1: string) => void
) & {
    __action__: "action1";
};

type Action2 = (
    (arg1: string, arg2: {a: string, b: number}) => void
) & {
    __action__: "action2";
};

const action1: Action1 = Object.assign(
    (arg1: string) => {},
    {__action__: "action1"} as const
);
const action2: Action2 = Object.assign(
    (arg1: string, arg2: {a: string, b: number}) => {},
    {__action__: "action2"} as const
);

const actions = [action1, action2];

type ActionsList = (Action1 | Action2)[];

const handleActions = (actions: ActionsList) => {
    const [action1, action2] = actions;
    if (action1.__action__ === "action1") {
        action1("x");   // <==== infers `(arg: string) => void` here
    }
};  

handleActions(actions);

Playground link


在您添加答案顶部引用的文本之前,只读 tuple type 是可能的。我将其保留在答案中以防它对其他人有用,即使它不适用于您的情况。

这是它的样子:

type ActionsList = readonly [
    (arg1: string) => void,
    (arg1: string, arg2: { a: string; b: number; }) => void
];

要设为只读,您需要 as const on actions:

const actions = [action1, action2] as const;
//                                ^^^^^^^^^

元组是一种"...数组类型,它确切地知道它包含多少元素,以及它在特定位置确切地包含哪些类型。"(来自link以上)

Playground link