我正在为项目的 Express 创建一个路由处理程序创建器,并且我正在尝试创建它,以便您可以在传递路由处理程序回调之前传递任意断言作为初始参数。像这样的东西:
const myHandler = makeHandler(assertion1(), assertion2(), (data, req, res, next) => {
// data is an array of the results of the assertions
});
我可以获得我想要的类型“工作”方式的某种版本:
// express namespace omitted for brevity; see playground link above.
type Assertion<T = unknown> = (req: express.Request) => T;
type ReturnTypes<A extends ReadonlyArray<Assertion>> = {
[K in keyof A]: ReturnType<A[K]>;
};
function assertion1<T extends object>(arg: T) {
return function inner(req: express.Request): T {
return arg;
}
}
function assertion2() {
return function inner(req: express.Request): string {
return "yes"
}
}
const a = assertion1({ something: "yes" })
const b = assertion2()
// This type works as expected: it is [{ something: string }, string]
type d = ReturnTypes<[typeof a, typeof b]>
但是,当我尝试让它作为上述
makeHandler
参数的可变参数版本工作时,有些东西似乎不起作用,上面示例中 data
的类型是 unknown[]
:
function makeHandler<
Assertions extends Assertion[],
R = ReturnTypes<Assertions>,
>(...assertionsAndCb: [...Assertions, HandlerCb<R>]) {
return async function handlerImpl(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const assertions = assertionsAndCb.slice(0, -1) as Assertions;
const [cb] = assertionsAndCb.slice(-1) as [HandlerCb<R>];
const data = [];
for (const assertion of assertions) {
try {
data.push(assertion(req));
} catch (e) {
next(e);
return;
}
}
try {
await cb(data as R, req, res, next);
} catch (e) {
next(e);
}
};
}
// `data` here doesn't seem to work, though. For some reason it's of type unknown[], rather than
// the same as type `d` above.
makeHandler(assertion1({ hey: "what"}), assertion2(), (data, req) => {
return { response: {} }
})
zip
这样的东西(并且我的函数很大程度上基于这个要点),但我正在努力让实际类型正确通过。我在这里遗漏了一些东西吗?例如,我没有正确地允许推断一些通用的东西?
function makeHandler<
Assertions extends Assertion[],
R = ReturnTypes<Assertions>,
>(...assertionsAndCb: [...Assertions, HandlerCb<R>]) : (
...args: [...Assertions, HandlerCb<R>]
) => ReturnType<HandlerCb<R>> {
return async function handlerImpl(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
const assertions = assertionsAndCb.slice(0, -1) as Assertions;
const [cb] = assertionsAndCb.slice(-1) as [HandlerCb<R>];
const data = [];
for (const assertion of assertions) {
try {
data.push(await assertion(req));
} catch (e) {
next(e);
return;
}
}
try {
await cb(data as R, req, res, next);
} catch (e) {
next(e);
}
};
}