Skip to content

Modules and imports ​

Programs are directory trees of .ts files. The compiler starts at the entry file you give it and compiles everything it imports into one binary. Module resolution is tsc's (moduleResolution: bundler), so specifiers are written the way TypeScript already accepts them: ./util, ./util.js, ./util.ts, ./dir/index.

ts
// Named, default and namespace imports; a re-export; type-only imports.
import { area, type Shape } from "./geometry.js";
import describe from "./describe";
import * as units from "./units";

const shapes: Shape[] = [
  { kind: "circle", size: 1 },
  { kind: "square", size: 3 },
];
for (const s of shapes) {
  console.log(describe(s), units.format(area(s), units.SQUARE_METERS));
}
ts
export interface Shape {
  kind: "circle" | "square";
  size: number;
}

export function area(s: Shape): number {
  return s.kind === "circle" ? Math.PI * s.size * s.size : s.size * s.size;
}
ts
import type { Shape } from "./geometry";

export default function describe(s: Shape): string {
  return `${s.kind}(${s.size})`;
}
ts
export { SQUARE_METERS } from "./constants";

export function format(value: number, unit: string): string {
  return `${Math.round(value * 1000) / 1000} ${unit}`;
}
ts
export const SQUARE_METERS = "m2";
text
circle(1) 3.142 m2
square(3) 9 m2

What is supported ​

  • Named imports and exports, export { x as y }, default exports and imports.
  • Namespace imports (import * as u from "./u"). They compile to static references, so the namespace must be used as u.name, not passed around as a value.
  • Re-exports: export * from "./m" and export { x } from "./m".
  • Type-only imports and exports (import type, import { type T }).
  • Built-in modules: node:fs, node:fs/promises and node:path, in named, default and namespace forms. See node:fs and node:path.
  • Packages in node_modules whose entry resolves to TypeScript source. They are compiled whole-program like your own files.

Every top-level symbol is namespaced by its module, and each module's top-level code runs once, in dependency order, as in Node.

What is rejected ​

FormCodeInstead
require, module.exports, export =CS1226ESM import / export
dynamic import()CS1226a static import
a package that ships only .js + .d.tsCS1226run that program on Node
using a namespace import as a valueCS1226refer to ns.member directly
reading a module variable before its declaration runsCS1228move the declaration up

node:process as an importable module is not supported yet; use the global process.

Next: Classes and interfaces.