chredv12
chredv12

Reputation: 3

Creating an extendable typescript package based on Koa with default and named exports

I am creating a module that extends Koa and adds some basic functionality, while still being extendable by the consumers of the package. This is proving to be very challenging so I hope someone has an idea on how this can be done.

End goal is to be able to retain types while using my package like this:

// Note default and named export - I have also tried only having named exports
import MyKoa, { isAuth } from 'mykoa';
import Router from 'koa-router';

// How I try to augment MyKoa in the consumer apps
declare module 'mykoa' {

  // Doesn't work: cannot use namespace "MyKoa" as value
  namespace MyKoa {
    interface Options {
      isReady: boolean;
    }
  }

  // Doesn't work at all
  interface Options {
    isReady: boolean;
  }
}


const app = new MyKoa();
const router = new Router();

router.get('/', isAuth(), ctx => {
  ctx.body = ctx.app.options.isReady; // Typescript should know that (ctx.)app.options.isReady is a boolean
});
app.use(router.routes());

app.listen(3000);

Currently I have 2 files:

src/lib/MyKoa.ts:

import Koa from 'koa';

// Augment Koa
declare module 'koa' {
  interface Application extends MyKoa {} // This ensures that MyKoa types are retained in koa-router middleware and works fine 
}

// My Koa class
class MyKoa extends Koa {
  public options: MyKoa.Options = {};
}

// Declare MyKoa namespace to hold relevant types
declare namespace MyKoa {
  interface Options {}
}

export = MyKoa;

src/index.ts:

import MyKoa from './lib/MyKoa';
import isAuth from './lib/isAuth';

export default MyKoa;
export { isAuth };

I can extend the types fine if I declare the module with full path to the files (e.g. mykoa/dist/lib/MyKoa), but I don't want the consumer of the package to rely on the internal directory structure of my package. Is this even possible? Is there a better way of providing the same functionality?

Upvotes: 0

Views: 318

Answers (1)

Matt McCutchen
Matt McCutchen

Reputation: 30949

The requirement for augmentations of a symbol to target the module that originally defines it and not a module that re-exports it is a current known limitation of TypeScript. The only workaround I can suggest is to move the symbols that you intend consumers of mykoa to augment into the main module of mykoa.

Upvotes: 1

Related Questions