Search code examples
typescriptes6-modulescommonjs

Can TypeScript import both ESM and CJS dependencies?


I am writing a TypeScript application that uses dependencies installed with NPM:

  • some of these dependencies export via CJS
  • some of these dependencies export via ESM

Is there a tsconfig.json configuration that would let me transparently use all these dependencies?


Solution

  • Yes it can!

    For example, this is a fresh ES Module project with an ESM dependency (chalk) and a common.js dependency (aes-js):

    package.json:

    {
      "type": "module",
      "private": true,
      "scripts": {
        "build": "tsc",
        "start": "yarn build && node dist/index.js"
      },
      "license": "ISC",
      "devDependencies": {
        "@types/aes-js": "^3.1.1",
        "typescript": "^4.8.4"
      },
      "dependencies": {
        "aes-js": "^3.1.2",
        "chalk": "^5.1.2"
      }
    }
    

    index.ts:

    import chalk from "chalk"
    import aesjs from "aes-js"
    
    console.log(chalk.blue(aesjs.utils.utf8.toBytes("this is a string")))
    

    tsconfig.json

    {
      "compilerOptions": {
        "target": "es2022",
        "module": "es2022",
        "esModuleInterop": true,
        "moduleResolution": "node",
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true,
        "outDir": "dist"
      }
    }
    

    Explanation: esModuleInterop is your friend. It makes sure that module.exports = gets turned into a default export (CommonJS -> ES6) and vice versa, making sure default exports are the same as module.exports = (ES6 -> CommonJS)