Search code examples
typescriptexpresstypescript2.0express-session

Convert static to dynamic import in Typescript


I have an Express.js application written in TypeScript (2.7.1) and I'm trying to dynamically import express-session module. In my understanding import session from 'express-session' should be equivalent to let session = await import('express-session') however the static import work just fine (if express-session is installed) while the dynamic version complains:

error TS2349: Cannot invoke an expression whose type lacks a call signature. 
Type '{ default: typeof session; Store: typeof Store; MemoryStore: typeof MemoryStore; }' 
has no compatible call signatures

Here is what my file looks like after removing the static import and surrounding the import with try-catch:

import express from 'express'

export class MyServer {
  public app: express.Application

  constructor() {
    this.app = express()
    this.init()
  }

  async init() {
    try {

      const session = await import('express-session')

      this.app.use(session({secret: 'my_secure_secret'}))
      this.app.set('hasSession', true)
    } catch (e) {
      console.log('Failed to load session, continue without it')
      this.app.set('hasSession', false)
    }
  }

Solution

  • The import() function does actually import the whole CommonJS exports object. Checking the types from @types/express-session we have:

    [...]
    declare function session(options?: session.SessionOptions): express.RequestHandler;
    
    declare namespace session {
      interface SessionOptions {
        secret: string | string[];
        name?: string;
        store?: Store | MemoryStore;
        cookie?: express.CookieOptions;
        genid?(req: express.Request): string;
        rolling?: boolean;
        resave?: boolean;
        proxy?: boolean;
        saveUninitialized?: boolean;
        unset?: string;
      }
    [...]
    export = session;
    

    At this point export = session actually is equivalent to exports.default = session (still a bit confusing that compiler understands session as reference of the function and not as namespace) and this leads to the solution:

      async init() {
        try {
    
          const session = (await import('express-session')).default
    
          this.app.use(session({secret: 'my_secure_secret'}))
          this.app.set('hasSession', true)
        } catch (e) {
          console.log('Failed to load session, continue without it')
          this.app.set('hasSession', false)
        }