gpt4 book ai didi

typescript - TS 4.0 可变元组和中间件模式

转载 作者:行者123 更新时间:2023-12-04 13:35:14 25 4
gpt4 key购买 nike

我想知道如何将可变参数元组与中间件模式一起使用,因为到目前为止我没有找到任何示例。
我有一个组成中间件的函数,如下所示:

const composeMiddleware = (...middlewares) => {
if (middlewares.length === 1) {
return middlewares[0];
}

return middlewares.reduceRight((f, next) => (...args) => next(f, ...args));
};
我是这样使用它的:
composeMiddleware(
(next, context) => next({ ...context, a: 1 }),
(next, context) => next({ ...context, b: 2 }),
({ a, b }) => a + b,
)({}); // => 3
我将如何键入此函数?似乎可变参数元组应该有所帮助

最佳答案

这是一场噩梦,我想。我不能 100% 确定我能想到的各种方法中的哪一种最接近匹配您的用例。在此过程中,您可能需要手动指定或手动注释类型;编译器不擅长推断高阶泛型函数类型的类型,例如 (next, context) => next({ ...context, a: 1 }) .

如果您愿意为您的函数提供强类型注释,如下所示:

const ret = composeMiddleware(
(next: ((x: { a: number }) => number), context: {}) => next({ ...context, a: 1 }),
(next: ((x: { a: number, b: number }) => number), context: { a: number }) => next({ ...context, b: 2 }),
({ a, b }: { a: number, b: number }) => a + b,
)({});
我可以想象有 composeMiddleware输入的强度足以验证/提示这是否有效并推断 composeMiddleWare() 的输出调用类型为 (i: {}) => number .但它很丑陋,使用了很多类型的杂耍。
既然你愿意考虑 variadic tuple types TS4.0 即将推出的功能,我会使用它,但我认为没有必要;它只是使访问诸如“元组的最后一个元素”之类的内容变得更容易。这是我的尝试:
type Last<T extends readonly any[]> = T extends [...infer _, infer L] ? L : never;
type Ret<T> = T extends (...x: any) => infer R ? R : never;
type PLast<T> = T extends (...x: [...infer _, infer P]) => any ? P : never;
type AsChain<T extends readonly ((...x: any) => any)[]> =
T extends [any, ...infer R] ? {
[K in keyof T]: K extends keyof R ? (n: (x: PLast<R[K]>) => Ret<Last<T>>, c: any) => Ret<Last<T>> :
(c: any) => any
} : never;
Last给你一个元组的最后一个元素。 Ret为您提供函数的返回类型或 never如果它不是一个函数。 PLast为您提供函数的最后一个参数或 never如果它不是一个函数。最后, AsChain<T>接受一个函数元组并尝试验证它是否是 composeMiddleWare() 的有效函数类型链.然后, composeMiddleWare()是这样输入的:
declare function composeMiddleware<T extends (readonly ((...x: any) => any)[])>(
...x: T & AsChain<T>): (i: PLast<T[0]>) => Ret<Last<T>>;
这里我们要求元组 T您传入的函数也匹配 AsChain<T> .我很想能够写 T extends AsChain<T>但这是一个无效的循环引用,所以我必须使用 ...x: T & AsChain<T>并在您做错事的情况下处理丑陋的错误消息。
如果您尝试使用 ret上面,编译器看到
/* const ret: number */
这是正确的......是的!
如果我在某处给出了一个错误的函数,比如使用该部分函数并输入 context{oops: number}而不是 {a: number} ,然后你会得到一个错误:
composeMiddleware(
(next: ((x: { a: number }) => number), context: {}) => next({ ...context, a: 1 }),
(next: ((x: { a: number, b: number }) => number), context: { oops: number }) => next({ ...context, b: 2 }),
({ a, b }: { a: number, b: number }) => a + b,
)({}); // error!
/*
Type '(next: (x: {a: number;}) => number, context: {}) => number' is not
assignable to type '(n: (x: { oops: number; }) => number, c: any) => number'.
Types of parameters 'next' and 'n' are incompatible.
Types of parameters 'x' and 'x' are incompatible.
Property 'oops' is missing in type '{ a: number; }'
but required in type '{ oops: number; }'.(2345)
*/
这是一个丑陋的错误,但它告诉你,因为第二个函数的上下文是 {oops: number} ,它期待第一个函数的 next()回调采取 {oops: number}参数,但它没有。因此,假设您理解错误,编译器会正确地告诉您调用有什么问题。又是吗?也许?

另一种可能的方法是当您调用 composeMiddleware 时您必须使用类型元组来指定它,例如 composeMiddleware<[{}, {a: number}, {a: number, b: number}, number]>(...)并且编译器将验证传入的函数是否正确运行。但是我已经花了很长时间来解决第一个解决方案,所以我不倾向于为第二个解决方案发疯。

我喜欢玩弄 TS 的类型系统,但我的直觉是该语言还没有真正为这种模式做好准备,至少我在生产级代码中使用的方式不是很舒服。
无论如何,希望有所帮助;祝你好运!
Playground link to code

关于typescript - TS 4.0 可变元组和中间件模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62545021/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com