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.

What You'll Learn

💡 Real-World Analogy

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:

🔎 Deep Dive: Module Resolution Basics

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.

Common Errors (and the fix)

Pro Tips

Frequently Asked Questions

Mini-Challenge: Counter Module

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.

🎉 Lesson Complete

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.

Continue this course