TypeScript 5.9 introduced support for import defer, a newer JavaScript proposal that lets a module be loaded without immediately running it. That sounds tiny, but it matters when a module is expensive or has side effects you do not always need.
This guide explains the idea without turning it into language-spec homework.
Quick answer
import defer lets you import a module namespace now, but delay evaluating that module until you actually use it.
normal imports vs deferred imports
Normal static imports are evaluated before your module runs. That is usually good because dependencies are ready and predictable.
Deferred imports are different. They can help when a module is only needed in a specific branch, but you still want a static-looking import shape instead of fully dynamic import().
| Import style | What happens |
|---|---|
import x from "./x" |
Dependency is loaded and evaluated before the current module runs |
await import("./x") |
Dependency is loaded later at runtime |
import defer * as x from "./x" |
Module namespace is available, but evaluation waits until usage |
small example
import defer * as reportTools from "./expensive-report-tools.js";
export function maybeBuildReport(userRole: string) {
if (userRole !== "admin") {
return "No report needed";
}
return reportTools.buildReport();
}
In this shape, the reporting module does not need to execute for the non-admin path. That can matter if the module does expensive setup, reads large data, registers side effects, or imports other heavy modules.
when it is useful
The best use case is not “make every import deferred.” That would make code harder to reason about.
It is useful when a dependency is expensive and used rarely, but you still want a static import structure. For example:
- Admin-only tools inside a large dashboard.
- Debug or diagnostics modules.
- Optional reporting and export logic.
- Heavy visualization helpers.
- Code that should avoid side effects until a branch is actually used.
what it does not fix
import defer is not a magic performance button. If the user always touches the deferred module during startup, you simply moved the cost slightly later.
It also does not replace good architecture. If a module has surprising side effects, the better long-term fix may be to remove those side effects or move them behind explicit functions.
practical checklist
- Use normal imports by default.
- Consider
import deferonly for expensive or side-effect-heavy modules. - Measure startup or interaction performance before and after the change.
- Keep deferred modules easy to identify.
- Avoid using it just because it is new.
Common mistakes
The first mistake is confusing deferred evaluation with lazy loading in every environment. Runtime support and bundler behavior matter, so check your actual target.
The second mistake is using it to hide messy module design. If importing a file does too much work, ask why that work happens at import time.
The third mistake is making debugging harder. Deferred work happens later, so errors may appear closer to the first use rather than during startup.
How to explain this in an interview
Say:
import deferhelps delay module evaluation for code paths that are not always used. I would use it carefully for expensive optional modules, then measure whether it improves the real user path.
That answer shows you understand the feature and the tradeoff.
Related guides
- TypeScript satisfies operator explained with practical examples
- TypeScript path aliases explained: baseUrl, paths, and runtime bugs
- TypeScript strict true explained
Sources checked
Final takeaway
import defer is useful when evaluation timing matters. Most code should keep normal imports, but expensive optional modules may benefit from being evaluated only when they are really needed.
Discussion
What would you try, change, or challenge after reading this guide? Specific results and errors help the next reader.
Comments will load as you reach this section.