mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2025-10-16 12:05:41 +00:00
Right now it's impossible to compose a bunch of middlewares, and
preserve their state/context type. It will be either erased and
converted to `any`, or will show an error.
For example, we have 3 middlewares:
```ts
type FooCtx = { foo: string };
type BarCtx = { bar: string };
type WooCtx = { woo: string };
const fooMiddleware: Koa.Middleware<FooCtx, {}> = async (ctx, next) => {
ctx.state.foo = 'foo';
await next();
};
const barMiddleware: Koa.Middleware<BarCtx, {}> = async (ctx, next) => {
ctx.state.bar = 'bar';
await next();
};
const wooMiddleware: Koa.Middleware<WooCtx, {}> = async (ctx, next) => {
ctx.state.woo = 'woo';
await next();
};
```
If we try to compose them together, we'll get an error:
```ts
const composed = compose([fooMiddleware, barMiddleware, wooMiddleware]);
// types of params context and context are incompatible
// Type ParameterizedContext<FooCtx, {}> is not assignable to
// ParameterizedContext<BarCtx, {}>
```
We can shut it up by providing `<any>` type parameter, but that will
erase their types:
```ts
const composed = compose<any>([fooMiddleware, barMiddleware, wooMiddleware]);
// `composed` type is `compose.ComposedMiddleware<any>`.
```
As a solution, I don't think there's a way to do typesafe `compose` for
variable number of middlewares, but we can overload `compose` and make a
typesafe one for 2 middlewares. You can then compose `compose`s to
compose more than 2 middleares :) Like, instead of:
```ts
compose([fooMiddleware, barMiddleware, wooMiddleware])
```
It will be:
```ts
compose([fooMiddleware, compose([barMiddleware, wooMiddleware]))
// `composed` type is `Middleware<ParameterizedContext<FooCtx & BarCtx & WooCtx, {}>`
```
What do you think?
52 lines
1.1 KiB
TypeScript
52 lines
1.1 KiB
TypeScript
import compose = require('koa-compose');
|
|
import * as Koa from "koa";
|
|
|
|
const fn1: compose.Middleware<any> = (context: any, next: () => Promise<void>): Promise<any> =>
|
|
Promise
|
|
.resolve(console.log('in fn1'))
|
|
.then(next);
|
|
|
|
const fn2: compose.Middleware<any> = (context: any, next: () => Promise<void>): Promise<any> =>
|
|
Promise
|
|
.resolve(console.log('in fn2'))
|
|
.then(next);
|
|
|
|
const fn = compose([fn1, fn2]);
|
|
|
|
interface FooCtx {
|
|
foo: string;
|
|
}
|
|
|
|
const fooMiddleware: Koa.Middleware<FooCtx> = async (ctx, next) => {
|
|
ctx.state.foo = "foo";
|
|
await next();
|
|
};
|
|
|
|
interface BarCtx {
|
|
bar: string;
|
|
}
|
|
|
|
const barMiddleware: Koa.Middleware<BarCtx> = async (ctx, next) => {
|
|
ctx.state.bar = "bar";
|
|
await next();
|
|
};
|
|
|
|
interface WooCtx {
|
|
woo: string;
|
|
}
|
|
|
|
const wooMiddleware: Koa.Middleware<WooCtx> = async (ctx, next) => {
|
|
ctx.state.woo = "woo";
|
|
await next();
|
|
};
|
|
|
|
new Koa<{}, {}>()
|
|
.use(compose([compose([fooMiddleware, barMiddleware]), wooMiddleware]))
|
|
.use(async (ctx, next) => {
|
|
ctx.state.foo;
|
|
ctx.state.bar;
|
|
ctx.state.woo;
|
|
ctx.body = "Something";
|
|
await next();
|
|
});
|