Page Content

Tutorials

Can I use CommonJS with TypeScript?

CommonJS with TypeScript

Large JavaScript codebases can be arranged using the fundamental idea of modules, which enables programmers to divide code into reusable, manageable chunks. TypeScript improves this procedure by offering control over the JavaScript output that is generated and supporting a number of module standards.

Understanding JavaScript Module Standards

Because JavaScript didn’t have a native module system in the past, a number of standards were developed to manage dependencies, chief among them CommonJS (CJS), Asynchronous Module Definition (AMD), and the more recent ES Modules (ESM).

CommonJS (CJS)

The module standard made popular by Node.js is called CommonJS.

  • Mechanism: CJS exposes functionality via module.exports or exports and imports dependencies using synchronous loading via the require() function.
  • Nature: CJS is ideal for server-side settings like Node.js, where files may be read instantly from the local disc, because loading is synchronous.
  • Usage: It emerged as the de facto standard for server-side JavaScript and module bundling.

ES Modules (ESM)

ECMAScript 2015 (ES6) introduced ES Modules as the official, standard module system.

  • Mechanism: ESM has a specific syntax, including imports for functionality consumption and exports for functionality exposure.
  • Nature: Because ESM is static by nature, imports and exports are defined at build time, enabling advanced tooling features like tree-shaking and static analysis.
  • Usage: ESM is the most recent standard for writing TypeScript and JavaScript code that is modular.

AMD (Asynchronous Module Definition)

AMD was widely utilised in browser settings; RequireJS was one such implementation.

  • Nature: AMD is asynchronous and required for browser module loading over a network connection.
  • Recommendation: The specifically state that AMD is “dead tech” and should not be used today. ES Modules are thought to be superior to SystemJS, another older experiment.

TypeScript’s Support for Modules

TypeScript maintains compatibility with earlier runtime environments, like as CJS, while supporting the native syntax of the ES Module.

ES Import/Export Syntax in TypeScript

Authoring all TypeScript code using the standard ES Module syntax (import and export) is the current best practice. An import or export statement at the root level of a TypeScript file is handled as a File Module (or external module), which establishes a local scope and prevents the global namespace from becoming contaminated.

The adaptable ES syntax for modules is fully supported by TypeScript:

  1. Named Exports: Prior to declarations, use the export keyword.
  2. Named Imports: To import certain bindings, use destructuring.
  3. Default Imports/Exports: You can import a single default item without curly brackets and export it.
  4. Bundled Imports: Importing all exports into a single namespace object is known as “bundled imports.”

Compiling to Different Module Systems via the Option

TypeScript uses the module compiler option in tsconfig.json to determine which JavaScript module system should be created in the output (.js) files when you write your code using the ES Module syntax.

The value chosen for the module option dictates how TypeScript converts your ES import/export statements into runtime code.

module Option ValueTarget SystemPrimary Use Case
commonjsCommonJSNode.js applications
amdAMDOlder browser loaders like RequireJS
es2015/esnextES ModulesModern bundlers (like Webpack/Rollup) or environments with native ESM support (Deno, newer Node.js)
umdUMDLibraries that need universal compatibility (browser or server)

The general recommendation, especially when targeting older Node.js environments or common bundling workflows, is often to use:

{
  "compilerOptions": {
    "module": "commonjs", /* Specify module code generation: 'commonjs' */ 
    "target": "es5"       /* Specify ECMAScript target version: 'ES5' */ 
  }
}

Setting module: "NodeNext" is advised if you are currently using Node.js since it preserves CommonJS compatibility while supporting the newest ES Module capabilities.

ESM to CommonJS

The TypeScript compiler converts the current ES syntax into the CJS structure (require and exports) when it comes across ES Module syntax in a file and is set up to produce CommonJS.

Consider two TypeScript files written using standard ES Module syntax, compiled with "module": "commonjs" and "target": "es5".

TypeScript Module ()

// math.ts (Input: ES Module Syntax)
export const PI = 3.14159;

export function add(a: number, b: number): number {
    return a + b;
}

JavaScript Output ()

The named exports are converted into properties on the CommonJS exports object:

// math.js (Output: CommonJS)
"use strict";
// Generated by typescript 
exports.PI = 3.14159; 
function add(a, b) {
    return a + b;
}
exports.add = add; 

TypeScript Consumer ()

// index.ts (Input: ES Module Syntax)
import { PI, add } from './math';

let result = add(10, 5);
console.log(`Result: ${result}, PI: ${PI}`);

JavaScript Output ()

The ES import is converted to a synchronous require call, capturing the exports into a local variable (math_1):

// index.js (Output: CommonJS)
"use strict";
var math_1 = require("./math"); // CJS require call

var result = (0, math_1.add)(10, 5);
console.log("Result: " + result + ", PI: " + math_1.PI);

/* Output when executed in Node.js runtime:
Result: 15, PI: 3.14159
*/

While guaranteeing that the produced code functions properly in settings that need CommonJS, this automatic transpilation enables developers to utilise the preferred, statically-friendly ES Module syntax.

Index