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.