Master

Modules and Namespaces

Organise large codebases with ES modules, declaration files, and module augmentation.

What you will see

saved: Ada

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Modules export values and types; other modules import them by name. `export` makes members public, `import` pulls them back, and only runtime exports are bundled. Multi-file import/export snippets are DISPLAY-ONLY, but the equivalent single-file version runs in Node.

1

ES module exports

function add(a, b) { return a + b; } const Point = null; console.log(add(2, 3)); const p = { x: 1, y: 2 }; console.log(p.x + p.y);

Before you run it, say out loud what it will print. Then open the answer.

What it prints
5 3
2

Default vs named exports

function greet() { return "hi"; } const version = "1.0"; console.log(greet(), version);

Before you run it, say out loud what it will print. Then open the answer.

What it prints
hi 1.0
3

Type-only imports

function db_save(u) { return "saved: " + u.name; } const u = { name: "Ada" }; console.log(db_save(u));

Before you run it, say out loud what it will print. Then open the answer.

What it prints
saved: Ada

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

A module exports declarations; other modules import them by name; the compiler resolves types while the runtime bundles only real values.
1 Module exports values and types
↓
2 Consumer imports with named or default syntax
↓
3 Compiler resolves types from the module
↓
4 Bundler includes only runtime exports
Why this works
Module resolution

TypeScript must find the types for an import. Configuration matters:

{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

NodeNext follows Node's real resolution rules, including package.json exports maps and requiring file extensions for ESM. Getting resolution right avoids Could not find a declaration file for module errors.

Declaration files

For untyped JavaScript, write a .d.ts file:

// foo.d.ts
declare function foo(x: number): string;
export { foo };

.d.ts files contain types only, no runtime code. Libraries ship them so TypeScript users get full intellisense and checking against plain JS.

Examples

ES module exports

Public API

// math.ts
export const add = (a: number, b: number): number => a + b;
export type Point = { x: number; y: number };

// main.ts
import { add, Point } from "./math";
const p: Point = { x: 1, y: 2 };

export makes declarations public; import brings them back. Only exported members are visible to other modules, everything else stays private.

Default vs named exports

Two export styles

export default function greet() { return "hi"; }   // default
export const version = "1.0";                      // named

A module has at most one default export but many named exports. Named imports use braces { version }; default imports do not. Mixing them is common for a primary export plus helpers.

Type-only imports

Avoid runtime cost for types

import type { User } from "./types";
import { db } from "./db";

function save(u: User) { db.save(u); }

import type is erased at compile time, so a type import never adds a runtime dependency. With verbatimModuleSyntax or isolatedModules, it is required to avoid shipping unused imports.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Exhaustiveness Checking
Previous Lesson
Exhaustiveness Checking
Next Lesson
Declaration Merging and Augmentation
Declaration Merging and Augmentation →