繁体   English   中英

我可以在 TypeScript 中表达这个高阶 function 签名吗?

[英]Can I express this higher-order function signature in TypeScript?

我有以下 function:

const fn = async (bool, num, str) => {
  // ...
};
  • bool是 boolean,
  • num是一个数字
  • str是一个可选字符串

我还有以下 HOF,如果调用者没有通过它,它允许在调用时填充最后一个可选参数:

const hof = (callback) => {
  return async (...args) => {
    if (args.length) {
      const lastArg = args[args.length - 1];
      if ("string" === typeof lastArg) {
        return callback(...args);
      }
    }
    const s = await findString();
    return callback(...[...args].concat(s));
  };
};

这允许编写:

const fn = hof(async (bool, num, str) => {
  // str will be valued, either by the caller, or by the hof
});

我如何才能完全输入hof ,知道它不知道回调的参数(即它们可能因一种用法而异)? TypeScript 甚至可以做到这一点吗?

我设法通过 generics 处理返回的类型:

const hof = <R>(
  callback: (...args: any) => Promise<R>
): (...args: any) => Promise<R> => {
  return async (...args) => {
    // ...
  };
};

但是可以键入那些动态参数,以便在使用时const fn = hof(async (bool, num, str) => {... }); , fn只会根据回调参数接受参数吗? fn签名和回调签名之间的唯一区别是fn允许未定义的str最后一个参数,而回调则强制它。

不是编译,而是类似的东西:

const hof = <R>(
  callback: (...args: ???, s: string) => Promise<R>
): (...args: ???, s?: string) => Promise<R> => {
  return async (...args) => {
    // ...
  };
};

以防万一我遇到XY 问题,实际用法是MySQL 事务管理 这种模式允许我在没有连接通过时自动启动一个新事务,或者如果它已经启动一个事务,则使用调用者的连接。 此处正在讨论此实现,但简而言之,我在现实生活中的hof

const transactional = (run) => async (...args) => {
  if (args.length) {
    const lastArg = args[args.length - 1];
    if (lastArg && "PoolConnection" === lastArg.constructor.name) {
      return run(...args);
    }
  }
  return _transactional(async (connection) => run(...[...args].concat(connection)));
};

如果您正在考虑更好的方法,请大喊;)

我认为您将很难在 typescript 中实现这一目标,至少,它会像现在这样困难。 我希望能够提供正确的答案,但我无法找到一个好的解决方案。

问题的一部分是试图定义一个 HOF,它采用任意长度和类型的 arguments,但始终使用字符串。 将函数定义为具有给定属性的对象会更容易一些,例如:

interface IWithConnection {
    connection?: string;
}

interface IFnArgs {
    bool: boolean;
    num: number;
}

const findString = async () => 'a-connection';

const injectConnection = async <T extends IWithConnection, U>(fn: (x: T) => U) => {
    return async (x: IWithConnection & T) => {
        if (x.connection !== undefined) {
            return fn(x);
        }

        const connection = await findString();

        return fn({ connection, ...x });
    };
};

const fn = ({ bool, num, connection }: IFnArgs & IWithConnection) => `${bool} ${num} ${connection}`;

const fn_ = injectConnection(fn);

我很欣赏这与您在问题中的实现完全不同,但这是我能得到的最接近的实现。

原始解决方案的问题在于,我们很难定义一个 function ,其参数为[string, ...T]因为我们无法传播 generics ,即使我们断言T extends [] 此外,我们不能将 args 定义为[...T[], string] ,因为 rest 参数必须是最后一个参数。

当 function 采用 arguments 所有相同类型时,我们可以提供一个可行的类型,因为我们可以将其键入为: [string, ...T[]] 不过,这将是非常有限的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM