JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeTopicstypescriptModule System
PrevNext
typescript
intermediate
9 min read

Module System

esm
export
import
import-type
module-resolution
modules
verbatim

TypeScript uses ES module syntax (import/export) with added features — import type ensures type-only imports are erased at runtime, module resolution strategies (node16, bundler) control how imports are resolved, and verbatimModuleSyntax enforces explicit type import annotations.

Key Points

1import type

Explicitly marks type-only imports for erasure at compile time — essential for single-file transpilers (esbuild, SWC) that can't do cross-file analysis.

2verbatimModuleSyntax

Requires explicit import type/export type annotations — ensures every import in output JavaScript is intentional, no accidental side-effect imports.

3Module Resolution Strategies

node16 for Node.js (requires .js extensions), bundler for frontend tools (extensionless), node for legacy CJS. Determines how imports map to files.

4Path Aliases

tsconfig paths map import specifiers to file paths (@/* → ./src/*), but the runtime/bundler needs its own corresponding alias configuration.

What You'll Learn

  • Explain import type and why it matters for modern bundlers
  • Choose the correct module resolution strategy for your project (node16, bundler)
  • Configure path aliases and understand the tsconfig/bundler dual configuration requirement
  • Know what verbatimModuleSyntax does and why it's recommended

Deep Dive

TypeScript's module system builds on JavaScript's ES modules with additional type-specific features. Understanding module resolution, type-only imports, and configuration is essential for structuring TypeScript projects.

ES Module Syntax

TypeScript uses standard ES module syntax — the same as modern JavaScript:

TypeScript
// Named exports
export function validate(input: string): boolean { ... }
export interface Config { port: number; host: string; }
 
// Named imports
import { validate, Config } from './validation';
 
// Default export/import
export default class App { ... }
import App from './app';
 
// Namespace import
import * as utils from './utils';

A file with any import or export statement is a module. Files without imports/exports are scripts — their declarations are global.

Type-Only Imports and Exports

import type explicitly marks imports as type-only — they're guaranteed to be erased at compile time:

TypeScript
import type { User, Config } from './types';
import { processUser, type ApiResponse } from './api';

Why this matters: bundlers like esbuild and SWC perform single-file transpilation — they can't determine if an import is only used as a type across the whole project. import type tells the transpiler definitively that the import is type-only and should be removed.

verbatimModuleSyntax

TypeScript 5.0 introduced verbatimModuleSyntax (replacing isolatedModules + importsNotUsedAsValues). When enabled, it requires explicit import type for type-only imports and export type for type-only exports. This ensures every import statement in the output JavaScript is intentional — no surprise import side effects, no reliance on cross-file analysis.

This is the recommended setting for projects using modern bundlers.

Module Resolution

Module resolution controls how TypeScript finds the file behind an import specifier. Key strategies:

  • node16/nodenext: Matches Node.js ESM resolution rules. Requires file extensions in relative imports (.js, not .ts). Respects exports in package.json. The standard for Node.js projects.
  • bundler: Matches how bundlers (Vite, webpack, esbuild) resolve imports. Allows extensionless imports. Respects exports in package.json. Best for frontend projects using a bundler.
  • node (legacy): Node.js CommonJS resolution. Tries adding .ts, .tsx, .js, /index.ts, etc. Doesn't support package.json exports.

The node16 strategy notably requires .js extensions in relative imports even though the source files are .ts — this matches Node.js runtime behavior where the compiled .js files are what actually resolve.

Path Aliases

paths in tsconfig maps import specifiers to file paths:

JSON
{ "paths": { "@/*": ["./src/*"], "@components/*": ["./src/components/*"] } }

Path aliases require a corresponding configuration in your bundler/runtime (webpack resolve.alias, Vite resolve.alias, or --loader in Node.js). TypeScript resolves types using paths, but the runtime needs its own mapping.

CommonJS Interop

TypeScript handles the ESM/CJS interop:

  • esModuleInterop: true enables default imports from CommonJS modules (import React from 'react' instead of import * as React from 'react')
  • allowSyntheticDefaultImports allows the syntax without emitting interop helpers

Most projects enable esModuleInterop — it's included in strict recommendations.

Key Interview Distinction

TypeScript uses ES module syntax with import type for compile-time-only imports. verbatimModuleSyntax enforces explicit type annotations so bundlers can safely remove type imports. Module resolution strategies (node16, bundler) determine how import specifiers map to files — choose based on your runtime/bundler. Path aliases need both tsconfig and bundler configuration.

Fun Fact

The node16 resolution strategy requires .js extensions in TypeScript imports (e.g., import { foo } from './utils.js' in a .ts file), which confuses many developers — why reference a .js file that doesn't exist yet? The answer is that TypeScript resolves based on the output file paths, not the source files. The .ts file will compile to .js, and at runtime Node.js needs the .js extension.

Learn These First

TypeScript Fundamentals

beginner

Basic Types

beginner

Continue Learning

Declaration Files

advanced

Interfaces vs Type Aliases

beginner

Practice What You Learned

How do TypeScript modules work and what's the difference between import types?
mid
modules
TypeScript supports ES6 modules with import/export syntax. Use 'import type' for type-only imports (erased at compile time). Declaration files (.d.ts) provide types for JavaScript libraries. Module resolution follows Node.js or bundler strategies.
Previous
Mapped Types
Next
Template Literal Types
PrevNext