在 Typescript 中将静态导入转换为动态导入

Convert static to dynamic import in Typescript

我有一个用 TypeScript (2.7.1) 编写的 Express.js 应用程序,我正在尝试动态导入 express-session 模块。在我的理解中 import session from 'express-session' 应该等同于 let session = await import('express-session') 但是静态导入工作正常(如果安装了 express-session)而动态版本抱怨:

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

这是我的文件在删除静态导入并用 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)
    }
  }

import() 函数实际上导入了整个 CommonJS exports 对象。检查来自 @types/express-session 的类型我们有:

[...]
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;

此时 export = session 实际上等同于 exports.default = session(编译器将 session 理解为 函数 的引用仍然有点令人困惑而不是 namespace),这导致了解决方案:

  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)
    }