TypeScript Modules

A quick guide to TypeScript modules, named exports, type-only imports, and path resolution.

TL;DR

  1. Share application code across files using export and import statements.
  2. Prefer named exports over default to simplify refactoring and autocompletion.
  3. Use import type to ensure zero runtime code overhead.

Named Exports and Imports

    named export

    Prefixes declarations with the export keyword to share them.

    // math.ts
    export const PI = 3.14159;
    export function double(n: number): number {
      return n * 2;
    }
    named import

    Pulls specific declarations into scope using curly braces.

    import { PI, double } from './math';
    console.log(double(10)); // 20
    renaming with as

    Aliases imported or exported identifiers to prevent collisions.

    import { double as multiplyByTwo } from './math';
    const val = multiplyByTwo(5);

Default Exports

    export default

    Designates the primary entry point of an individual module.

    // logger.ts
    export default function log(msg: string): void {
      console.log(`[APP]: ${msg}`);
    }
    default import

    Imports the default entity without using enclosing curly braces.

    import log from './logger';
    log('System initialized');
    mixed exports

    Combines a default export alongside auxiliary named exports.

    // client.ts
    export const timeout = 5000;
    export default class ApiClient {}

Type-Only Imports and Exports

    import type

    Imports types strictly without emitting any JavaScript runtime code.

    import type { User } from './types';
    let currentUser: User;
    inline type modifier

    Differentiates values and types within a single import statement.

    import { type User, fetchUser } from './users';
    const user: User = await fetchUser('101');
    export type

    Re-exports type signatures from internal contracts safely.

    export type { UserConfig, AuthToken } from './auth';

Re-Exporting and Barrels

    barrel export

    Aggregates multiple submodules into a unified entry point.

    // components/index.ts
    export { Button } from './Button';
    export { Modal } from './Modal';
    export wildcard

    Re-exports every named export from a specified target file.

    export * from './constants';
    export * from './helpers';
    renamed re-export

    Exposes an internal default export as a clean named export.

    export { default as Header } from './Header';

Module Resolution and Paths

    relative paths

    References adjacent files using explicit directory indicators.

    import { utils } from './subfolder/utils';
    import { parentConfig } from '../config';
    bare specifiers

    Loads packages installed inside the node_modules folder.

    import React from 'react';
    import { z } from 'zod';
    path aliases

    Shortens deeply nested imports using configured prefix aliases.

    // Configured via tsconfig paths
    import { formatPrice } from '@/lib/utils';

Tips

  1. Always use import type when bringing in interfaces to prevent bundlers from including unnecessary JavaScript overhead.
  2. Keep barrel index.ts files focused on cohesive subdirectories to avoid triggering circular dependencies during bundle compilation.

Warnings

  1. Remember that tsconfig.json path aliases only satisfy the type checker and still require bundler resolution plugins.
  2. Avoid using wildcard export * barrels in large libraries because they frequently defeat bundler tree-shaking optimizations.

In Practice

FAQ