1. Introduction
A module in TypeScript is any file that contains a top-level import or export statement. Modules let you split a program into independent, reusable pieces, each with its own scope, so identifiers declared in one file do not leak into another unless explicitly exported and imported. TypeScript builds directly on the ECMAScript (ES) module syntax standardized in JavaScript, while adding type-aware features such as type-only imports/exports and compile-time checking of what a module exposes.
Cricket analogy: A TypeScript module is like a single team's dressing room: players (variables) declared inside stay private to that team unless the captain explicitly "exports" them onto the team sheet for other squads to "import" and select.
Before ES modules were standardized, TypeScript supported its own module systems (CommonJS, AMD, UMD, SystemJS) via the module compiler option, and it still emits code targeting these formats when needed for Node.js or bundlers. Understanding modules is essential for any real-world TypeScript project because virtually all modern codebases are organized as a graph of import/export relationships.
Cricket analogy: Before the ICC standardized T20 rules globally, boards ran their own domestic formats like the old Ranji knockout structure; similarly TypeScript's module option can still emit CommonJS or AMD for older Node/bundler leagues even though ES modules are now the standard format everyone plays by.
2. Syntax
// mathUtils.ts — exporting
export const PI = 3.14159;
export function square(n: number): number {
return n * n;
}
export interface Point {
x: number;
y: number;
}
export default class Calculator {
add(a: number, b: number): number {
return a + b;
}
}
// app.ts — importing
import Calculator, { PI, square, Point } from "./mathUtils";
import type { Point as PointType } from "./mathUtils"; // type-only import
import * as MathUtils from "./mathUtils"; // namespace-style import of all exports
const calc = new Calculator();
console.log(calc.add(2, 3), PI, square(4));3. Explanation
ES modules (import/export) are the modern standard for organizing TypeScript and JavaScript code, and TypeScript's compiler understands them natively. A file can have multiple named exports (export const, export function, export interface) and at most one default export. Named exports are imported with matching names inside { }, while a default export is imported without braces under any local name you choose. The import * as Name form collects every export of a module into a single namespace object, which is useful when a module has many related exports.
Cricket analogy: A module with named exports like export const strikeRate and export function economyRate mirrors a scorecard with multiple named stats you import individually, while a single export default battingOrder is like the one headline stat pulled in under any name; import * as Stats from "./match" collects the whole scorecard into one namespace object.
TypeScript also supports import type and export type, which are erased entirely at compile time — they exist only to communicate to the compiler (and to bundlers using isolatedModules) that the imported symbol is a type, not a runtime value. This matters because mixing type and value imports carelessly can cause runtime errors when a bundler tries to resolve a type-only name as an actual JavaScript import.
Cricket analogy: import type { Scorecard } is like a scout's notebook entry that only exists on paper, never printed onto the physical team sheet handed to the umpire; mixing it up with a real player import can cause the bundle to reference a name that was never actually on the field.
A file with no import or export statements is treated by TypeScript as a legacy "script" whose top-level declarations are global, not a module. Adding even a single export {} at the bottom of a file is a common trick to force it into module scope without exporting anything meaningful.
Gotcha: importing an interface or type alias using a plain import { Point } from "./mathUtils" (instead of import type) works today under default settings, but if isolatedModules is enabled (common in Babel/esbuild/SWC pipelines) or verbatimModuleSyntax is set, type-only symbols imported without import type can cause build failures or leave a dangling runtime import for a symbol that does not exist in the compiled JavaScript. Always use import type for types when you know a symbol is type-only.
4. Example
// logger.ts
export interface Logger {
log(message: string): void;
}
export function createConsoleLogger(): Logger {
return {
log: (message: string) => console.log(`[LOG] ${message}`),
};
}
// index.ts
import type { Logger } from "./logger";
import { createConsoleLogger } from "./logger";
const logger: Logger = createConsoleLogger();
logger.log("Application started");5. Output
When compiled with `"module": "ESNext"` and run, index.ts prints:
[LOG] Application started
At compile time, `import type { Logger }` is completely erased from the emitted JavaScript for index.js — only the `createConsoleLogger` import remains as a real runtime import, because `Logger` is a type and has no runtime representation.6. Key Takeaways
- A TypeScript file becomes a module the moment it has a top-level import or export; otherwise it is treated as a global script.
- Named exports use
{ }on import and can coexist with a singledefaultexport per file. import type/export typeare erased at compile time and should be used for type-only symbols, especially underisolatedModules.import * as Namegathers all exports of a module into one namespace-like object.- TypeScript emits different runtime module formats (CommonJS, ESNext, etc.) based on the
modulecompiler option, independent of the source syntax you write.
Practice what you learned
1. What determines whether a TypeScript file is treated as a module rather than a global script?
2. Why can omitting `import type` for a type-only symbol cause problems under `isolatedModules`?
3. How many default exports can a single TypeScript module have?
4. What does `import * as MathUtils from "./mathUtils"` do?
5. Which compiler option controls the runtime module format (e.g., CommonJS vs ESNext) that TypeScript emits?
Was this page helpful?
You May Also Like
Namespaces in TypeScript
TypeScript's legacy 'internal modules' feature for grouping related code under a single global name, and why ES modules are now preferred.
Declaration Files (.d.ts) in TypeScript
How .d.ts files describe the shape of JavaScript code for the TypeScript compiler, including DefinitelyTyped (@types) packages.
Configuring tsconfig.json in TypeScript
How tsconfig.json controls TypeScript compiler behavior, including strictness, target/module output, file inclusion, and output directories.
Interfaces in TypeScript
Learn how TypeScript interfaces describe the shape of objects using structural typing, and why they are the primary tool for contract-based design.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics