Modules & Declaration Files
A module is a single file that uses export to share specific values and types and import to pull them into other files, while a declaration file ( .d.ts ) supplies type information for code that already exists without emitting any runtime JavaScript.
Learn Modules & Declaration Files in our free TypeScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free TypeScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
A module is a shop with a storefront and a stockroom . The items on display (your export s) are what customers can buy; everything in the back (un-exported helpers) is private and stays out of sight. An import is a customer walking in and taking a specific labelled item off the shelf. A barrel file is a shopping mall directory that lists where to find products from several shops in one place. A .d.ts declaration file is the price-and-spec sheet for goods made in another factory: it tells you exactly what each item is, even though it wasn't manufactured here.
1. Exports, Imports & the Public Surface
Each file is its own scope. Whatever you mark with export becomes visible to other files; everything else stays private to that file. You then bring names in with import . Here is real TypeScript module syntax across two files:
The runnable box uses the module pattern to show the same public/private split inside one file.
2. Default vs Named Exports & Re-exports
A module can have at most one export default plus any number of named exports. The default is imported under a name you choose; named exports keep their names (or are aliased with as ). A barrel file re-exports several modules so consumers import from one path:
3. Type-Only Imports & Declaration Files
import type brings in something used purely as a type and is erased completely from the output — zero runtime cost, no circular-import surprises. A .d.ts file uses declare to describe code that already exists at runtime (a global, a plain-JS library) so the compiler can type-check against it:
When you write import x from "..." , TypeScript has to find the file. The specifier tells it how:
The moduleResolution compiler option (commonly "bundler" or "node16" ) controls the exact lookup rules — you'll meet it in the next lesson on tsconfig .
🎯 Your Turn
Build a small string-utilities module with the module pattern: return the functions you want to "export" and list the public surface. Fill in the blanks and match the expected output.
Build a counter "module" that keeps its count private and exposes only increment , decrement , and value — exactly the encapsulation a real module file provides. Follow the outline, run it, and match the example output.
Practice quiz
In a module, what makes a value visible to other files?
- Declaring it with var
- Putting it at the top of the file
- Marking it with export
- Naming it in capitals
Answer: Marking it with export. Whatever you mark with export becomes the file's public surface; everything else stays private.
How many default exports may a module have?
- At most one
- Unlimited
- Exactly two
- None allowed
Answer: At most one. A module can have at most one export default, plus any number of named exports.
How do you import a default export?
- import { thing } from './mod'
- import default thing from './mod'
- require('./mod')
- import thing from './mod'
Answer: import thing from './mod'. A default is imported without braces and you choose the name: import thing from './mod'.
When should you use import type?
- For every import
- When importing something used only in type positions
- Only for default exports
- Never in modern code
Answer: When importing something used only in type positions. import type is for type-only imports; it is fully erased and pulls in no runtime code.
What is a barrel file?
- An index.ts that re-exports the public API of a folder
- A file that stores binary data
- A compiled output file
- A test fixture
Answer: An index.ts that re-exports the public API of a folder. A barrel re-exports several modules from one place so consumers import from a single path.
What does a .d.ts declaration file contain?
- Runtime code and types
- Compiled JavaScript
- Only type declarations, no runtime code
- CSS definitions
Answer: Only type declarations, no runtime code. A .d.ts file holds only type declarations describing code that exists elsewhere.
What does the declare keyword emit at runtime?
- A global variable
- Nothing; it only informs the type-checker
- A function stub
- An import statement
Answer: Nothing; it only informs the type-checker. declare adds no runtime code; it tells the compiler something already exists.
A file with no top-level import or export is treated as what?
- A module with its own scope
- An error
- A barrel
- A script whose declarations leak into global scope
Answer: A script whose declarations leak into global scope. Without an import/export it is a script; add export {} to make it a proper module.
How is a relative specifier like './utils' resolved?
- From node_modules
- Against the current file's folder
- Via tsconfig paths only
- From the project root always
Answer: Against the current file's folder. Relative specifiers are resolved against the importing file's directory.
Why prefer named exports over a default for most code?
- They run faster
- They allow more than one per file only as default
- They autocomplete, rename safely, and tree-shake well
- They are required by strict mode
Answer: They autocomplete, rename safely, and tree-shake well. Named exports refactor, auto-import, and tree-shake better; reserve default for a file's main thing.