SolvedDefinitelyTyped [@types/koa-router] incompatible type with koa

If you know how to fix the issue, make a pull request instead.

Source Code

import Router from 'koa-router'
import { Context } from 'koa'

const router = new Router()

router.get(
  '/auth',
  async (ctx: Context): Promise<void> => {
    ctx.body = 'aaa'
  }
)

Error Message

Argument of type '(ctx: Context) => Promise<void>' is not assignable to parameter of type 'Middleware<ParameterizedContext<any, IRouterParamContext<any, {}>>>'.
  Types of parameters 'ctx' and 'context' are incompatible.
    Property 'log' is missing in type 'BaseContext & { app: import("/Users/li.li/Work/poking/node_modules/@types/koa/index.d.ts")<any, {}>; request: Request; response: Response; req: IncomingMessage; res: ServerResponse; ... 4 more ...; respond?: boolean | undefined; } & IRouterParamContext<...>' but required in type 'Context'.ts(2345)
26 Answers

✔️Accepted Answer

koa provides us with multiple types for ctx. At this moment we only need to care about Context and ParameterizedContext
Some libraries extends Context by typescript declaration merging. For example, koa-sessiondoes it

declare module "koa" {
    interface Context {
        session: session.Session | null;
        readonly sessionOptions: session.opts | undefined;
    }
}

Fortunately or unfortunately koa-router uses ParameterizedContext instead of Context for typing ctx object in middlewares (actually it's provided by koa, but it doesn't matter here). That is why the code bellow shows an error

import Router from 'koa-router';
import { Context } from 'koa';

const router = new Router();

router.post('/', (ctx: Context) => { // error: Argument of type '(ctx: Context) => void' is not assignable to parameter of type 'Middleware<ParameterizedContext<any, IRouterParamContext<any, {}>>>' because it is missing the following properties from type 'Context': session, sessionOptions
    ctx.body = 'Hello worls';
});

To fix this problem we just need to use capabilities of generic Router class

import Router from 'koa-router';
import { DefaultState, Context } from 'koa';

const router = new Router<DefaultState, Context>();

router.post('/', (ctx: Context) => {
    ctx.body = 'Hello world';
});

Some people recommend to use BaseContext (or DefaultContext in new versions). I recommend to avoid it, because it simply has [key: string]: any; that allows to put whatever you want. Typescript was actually developed for avoiding such things

You may not have such an issue if you use @types/koa@2.0.50. But it's because the same [key: string]: any; which was moved to DefaultContext in new versions

Other Answers:

A note for users defining their route callback outside the route (like this)

function getUser(ctx: Context){...}

router.post('/user/:id', getUser)

In that case typescript won't know about the declaration merging and Context won't contain router specific properties (like ctx.params)

One solution is to use a custom defined "RouterContext" that applies the type mentioned by @zfeed :

export type RContext = ParameterizedContext<
  DefaultState,
  Context & Router.IRouterParamContext<DefaultState, Context>
>;

function getUser(ctx: RContext) {
  ctx.params; // <--- this will be found
}

And now both router properties and koa will be available as RContext

For anyone still stuck on this issue, simply removing the type definition on the ctx parameter fixes the problem:

baseRouter.get( '/auth', async (ctx): Promise<void> => { ctx.body = 'aaa' } )

I found another way to fix this issue--blow away node_modules and your lock file (if any) and re-install everything.

The problem is all the @types koa additives like koa-router, koa-compose and kao-websockets all include a dependency on "@types/koa": "*". NPM/Yarn downloads these at the current version and stores them local to the Koa-router types module.

When one updates the koa types to a new version, the old downloads for additive packages are not updated too. Deleting the node_modules and lock files causes npm or yarn to redownload everything to the same version of @types/koa.

@lili21
koa-router call the handler function with RouterContext type , but the handler function we defined need the Context type. Context type maybe defined some other properties which are not in RouterContext, so the type checking can not success.

we can resolve this with Intersection Types.

import { RouterContext } from "koa-router";
router.get(
  '/auth',
  async (ctx: Context & RouterContext): Promise<void> => {
    ctx.body = 'aaa'
  }
)

More Issues: