J. *_*ers 5 functional-programming typescript typescript-typings
我有一个asyncPipe这样的功能:
export function asyncPipe<A, B>(
ab: (a: A) => MaybePromise<B>
): (a: MaybePromise<A>) => Promise<B>;
export function asyncPipe<A, B, C>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>
): (a: MaybePromise<A>) => Promise<C>;
export function asyncPipe<A, B, C, D>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>
): (a: MaybePromise<A>) => Promise<D>;
export function asyncPipe<A, B, C, D, E>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>,
de: (d: D) => MaybePromise<E>
): (a: MaybePromise<A>) => Promise<E>;
export function asyncPipe(...fns: Function[]) {
return (x: any) => fns.reduce(async (y, fn) => fn(await y), x);
}
Run Code Online (Sandbox Code Playgroud)
我想这样称呼它:
const createRoute = (...middleware: Middleware[]) => async (
request: Response,
response: Request
) => {
try {
await asyncPipe(...middleware)({
request,
response
});
} catch (e) {
console.error(e);
response.status(500);
response.json({
error: "Internal Server Error"
});
}
};
Run Code Online (Sandbox Code Playgroud)
whereMiddleware是函数的一种类型,它接收至少具有键request和response的对象,但可能会向该对象添加更多数据。
{
request,
response,
user, // added by some middleware
db, // added by another middleware
/* ... more keys added by middleware */
}
Run Code Online (Sandbox Code Playgroud)
打字稿抱怨说 Expected 1-6 arguments, but got 0 or more.在我调用的行中asyncPipe。我怎么能告诉它,这middleWare总是至少一个参数,或者重载它,所以它也接受 0 个参数?
更新:
我试过 Rob 的回答是这样的:
export function asyncPipe<A>(...fns: Function[]): MaybePromise<A>;
Run Code Online (Sandbox Code Playgroud)
但这可以防止我所有的asyncPipe特定测试失败。顺便说一句,测试看起来像这样:
import { describe } from "riteway";
import { asyncPipe } from "./asyncPipe";
const asyncInc = (n: number) => Promise.resolve(n + 1);
const inc = (n: number) => n + 1;
describe("asyncPipe()", async assert => {
assert({
given: "a promise",
should: "pipe it",
actual: await asyncPipe(asyncInc)(1),
expected: 2
});
assert({
given: "two promises",
should: "pipe them",
actual: await asyncPipe(asyncInc, asyncInc)(1),
expected: 3
});
assert({
given: "three promises",
should: "pipe them",
actual: await asyncPipe(asyncInc, asyncInc, asyncInc)(1),
expected: 4
});
assert({
given: "promises mixed with synchronous function",
should: "pipe them",
actual: await asyncPipe(asyncInc, inc, asyncInc)(1),
expected: 4
});
{
const throwInc = (n: number) => Promise.reject(n + 1);
assert({
given: "promises where one throws",
should: "pipe them",
actual: await asyncPipe(asyncInc, throwInc, asyncInc)(1).catch(x => x),
expected: 3
});
}
});
Run Code Online (Sandbox Code Playgroud)
我做了一些额外的研究,并在这里获取了一些答案来做出我自己的答案。您现在有两个选择:
一种方法是将函数的代码写入函数asyncPipe中createRoute:
const createRoute = (...middleware: Middleware[]) => async (
request: Response,
response: Request
) => {
try {
middleware.reduce(async (y, fn) => fn(await y), { request, response });
} catch (e) {
console.error(e);
response.status(500);
response.json({
error: "Internal Server Error"
});
}
};
Run Code Online (Sandbox Code Playgroud)
这肯定会起作用,但我不确定你是否有这种类型检测。尝试第二个选项:
第二种方法稍微复杂一些。它将涉及复杂的类型,但我认为这正是您想要的。(此代码取自Arturs的答案并修改)
type MaybePromise<T> = T | Promise<T>
export function asyncPipe<A, B>(
ab: (a: A) => MaybePromise<B>
): (a: A) => Promise<B>;
export function asyncPipe<A, B, C>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>
): (a: A) => Promise<C>;
export function asyncPipe<A, B, C, D>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>
): (a: A) => Promise<D>;
export function asyncPipe<A, B, C, D, E>(
ab: (a: A) => MaybePromise<B>,
bc: (b: B) => MaybePromise<C>,
cd: (c: C) => MaybePromise<D>,
de: (d: D) => MaybePromise<E>,
): (a: A) => Promise<E>;
export function asyncPipe<FS extends any[]>(...fns: AsyncParams<FS>): AsyncPipeReturnType<FS>
export function asyncPipe (...fns: AsyncParams<any[]>) {
if (fns.length === 0) return () => Promise.resolve(null)
return (x: Parameters<typeof fns[0]>[0]) => fns.reduce(async (y, fn) => fn(await y), x)
}
type Callback = (a: any) => MaybePromise<unknown>;
type FunToReturnType<F> = F extends Callback ? ReturnType<F> extends Promise<infer U> ? U : ReturnType<F> : never;
type EmptyPipe = (a: never) => Promise<never>
type AsyncPipeReturnType<FS extends Callback[], P = Parameters<FS[0]>[0]> =
FS extends [...infer _, infer Last] ? (a: P) => Promise<FunToReturnType<Last>> : EmptyPipe;
type AsyncParams<FS extends Callback[], P = Parameters<FS[0]>[0]> =
FS extends [infer H, ...infer Rest] ?
H extends (p: P) => unknown ?
Rest extends Callback[] ?
[H, ...AsyncParams<Rest, FunToReturnType<H>>]
: [{ error: "__A_PARAMETER_NOT_A_FUNCTION__" }, ...Rest]
: [{ error: "__INCORRECT_FUNCTION__", provided: H, expected_parameter: P }, ...Rest]
: FS;
Run Code Online (Sandbox Code Playgroud)
你可以在这里尝试一下
无法自动推断 FS 类型。
如果您希望下一个中间件了解前一个中间件,则必须稍微修改类型并将其添加到函数中createRoute。
| 归档时间: |
|
| 查看次数: |
147 次 |
| 最近记录: |