[英]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.