TypeScript uses the same ES module syntax as modern JavaScript. Modules keep code organized, testable, and reusable across files.

Named Exports

Export individual bindings from a file:

  // math.ts
export const PI = 3.14159;

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

export function multiply(a: number, b: number): number {
    return a * b;
}
  

Import named exports:

  // app.ts
import { PI, add, multiply } from './math.js';

console.log(PI);              // 3.14159
console.log(add(2, 3));       // 5
console.log(multiply(4, 5));  // 20
  

Use .js extensions in import paths when targeting ES modules in Node.js — TypeScript resolves them to the corresponding .ts source files.

Default Exports

One default export per file:

  // logger.ts
export default class Logger {
    log(message: string): void {
        console.log(`[LOG] ${message}`);
    }
}
  
  // main.ts
import Logger from './logger.js';

const logger = new Logger();
logger.log('Application started');
  

Prefer named exports for better refactoring and autocomplete. Default exports are common in React components.

Re-exports

Aggregate modules into a single entry point:

  // utils/index.ts
export { add, multiply } from './math.js';
export { formatDate } from './date.js';
export type { User } from './types.js';
  

Consumers import from the barrel file:

  import { add, formatDate } from './utils/index.js';
  

Type-Only Imports and Exports

Use import type and export type to strip types at compile time:

  // types.ts
export interface User {
    id: number;
    name: string;
}

export type Role = 'admin' | 'user';
  
  // user-service.ts
import type { User, Role } from './types.js';

export function createUser(name: string, role: Role): User {
    return { id: Date.now(), name };
}
  

Type-only imports prevent accidental runtime dependencies on erased types.

Module Resolution

Configure how TypeScript resolves imports in tsconfig.json:

  {
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
  

Path aliases simplify imports:

  import { User } from '@/models/user.js';
  

For Node.js without a bundler, use "moduleResolution": "NodeNext" and include file extensions in imports.

CommonJS Interop

When importing CommonJS modules in a TypeScript ES module project:

  import express from 'express';  // requires "esModuleInterop": true
  

Enable "esModuleInterop": true in tsconfig.json for smoother default import syntax.

Prefer ES modules for new code. Next, we learn how to narrow types at runtime with type guards.