From 66001ee7644cbbc4101265cbb4be96a730114fb0 Mon Sep 17 00:00:00 2001 From: Anton Astashov Date: Thu, 31 Jan 2019 09:03:56 -0600 Subject: [PATCH] [@types/koa-compose]: Provide way for typesafe compose 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 = async (ctx, next) => { ctx.state.foo = 'foo'; await next(); }; const barMiddleware: Koa.Middleware = async (ctx, next) => { ctx.state.bar = 'bar'; await next(); }; const wooMiddleware: Koa.Middleware = 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 is not assignable to // ParameterizedContext ``` We can shut it up by providing `` type parameter, but that will erase their types: ```ts const composed = compose([fooMiddleware, barMiddleware, wooMiddleware]); // `composed` type is `compose.ComposedMiddleware`. ``` 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` ``` What do you think? --- types/koa-compose/index.d.ts | 7 +++++ types/koa-compose/koa-compose-tests.ts | 38 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/types/koa-compose/index.d.ts b/types/koa-compose/index.d.ts index 88047abcc9..f35b9ca208 100644 --- a/types/koa-compose/index.d.ts +++ b/types/koa-compose/index.d.ts @@ -2,6 +2,13 @@ // Project: https://github.com/koajs/compose // Definitions by: jKey Lu // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +import * as Koa from "koa"; + +declare function compose( + middleware: [Koa.Middleware, Koa.Middleware] +): Koa.Middleware; declare function compose(middleware: Array>): compose.ComposedMiddleware; diff --git a/types/koa-compose/koa-compose-tests.ts b/types/koa-compose/koa-compose-tests.ts index ed1c705e5d..56b105e900 100644 --- a/types/koa-compose/koa-compose-tests.ts +++ b/types/koa-compose/koa-compose-tests.ts @@ -1,4 +1,5 @@ import compose = require('koa-compose'); +import * as Koa from "koa"; const fn1: compose.Middleware = (context: any, next: () => Promise): Promise => Promise @@ -11,3 +12,40 @@ const fn2: compose.Middleware = (context: any, next: () => Promise): .then(next); const fn = compose([fn1, fn2]); + +interface FooCtx { + foo: string; +} + +const fooMiddleware: Koa.Middleware = async (ctx, next) => { + ctx.state.foo = "foo"; + await next(); +}; + +interface BarCtx { + bar: string; +} + +const barMiddleware: Koa.Middleware = async (ctx, next) => { + ctx.state.bar = "bar"; + await next(); +}; + +interface WooCtx { + woo: string; +} + +const wooMiddleware: Koa.Middleware = 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(); + });