TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
167: When to Still Use Namespaces
A few years ago, I was tasked with migrating a massive, decade-old internal dashboard to TypeScript. The project relied on a proprietary charting library written back when "modules" meant just dropping five <script> tags in the HTML head in a very specific order. The library didn't export anything; it just dumped a giant ChartEngine object onto the window. Every time I tried to use a standard ES module import, the compiler screamed at me because the library didn't have a package.json or any export statements. I spent two days fighting the build system before I realized I was trying to use a modern tool for a legacy problem.
In modern TypeScript, we almost always use ES modules (import and export). If you're starting a new project today, you'll probably never write the namespace keyword. But namespaces aren't "deprecated" in the sense that they're gone; they just serve a different purpose now. Think of a namespace as a way to group related code under a single name to avoid polluting the global scope, specifically when you aren't in a module-based environment.
Mapping Global Legacy Libraries
The most common reason I still reach for namespaces is when writing declaration files (.d.ts) for libraries that don't use modules. When a library attaches itself to the global window object, a standard export won't work because the library isn't a module—it's a global script.
This is where declare namespace comes in. It tells TypeScript: "Hey, there is a global object with this name, and here is the shape of everything inside it." For that legacy charting library, my solution looked like this:
declare namespace ChartEngine {
interface Options {
width: number;
height: number;
theme: 'dark' | 'light';
}
function render(elementId: string, data: any[], options: Options): void;
function clearAll(): void;
}
By doing this, I could type ChartEngine.render(...) anywhere in my app without an import statement, and TypeScript knew exactly what the Options interface looked like. It mirrored the reality of the runtime environment perfectly.
Internal Grouping and the Module Trade-off
You might occasionally see namespaces used inside a single file to organize a huge set of related types. For instance, if you have a complex API response with fifty different nested interfaces, wrapping them in a namespace can keep your autocomplete clean. Instead of seeing UserAddress, UserPreferences, and UserSecurity all floating in the top-level scope, you get User.Address, User.Preferences, and so on.
However, I'll give you a word of caution: don't let this become a habit in your application logic. Using namespaces for organization in a project that already uses ES modules is usually an anti-pattern. If your file is getting so large that you need namespaces to organize your types, the real solution is to break that file into smaller, focused modules. Namespaces create a "closed" world that can make tree-shaking harder for bundlers like Webpack or Vite. I've seen projects where the bundle size stayed bloated because the compiler couldn't prove that a specific part of a namespace wasn't being used elsewhere.
Stick to namespaces when you are describing a world you didn't create (like a global legacy library) or when you are working in an environment that absolutely cannot support modules. In every other case, a folder full of small files and explicit imports is the way to go.
📋 Practical Task
Defining Types for the "LegacyAnalytics" Global SDK
Imagine you've just joined a project that uses an ancient analytics SDK. The SDK is loaded via a script tag and creates a global object called LegacyAnalytics. It has a method trackEvent that takes a string event name and an object of properties, and a setUser method that takes a user ID and an email.
Your task is to create a declaration file (or a block of code) using a namespace that describes this global object so that the rest of the team gets full IntelliSense and type checking when calling LegacyAnalytics.trackEvent() and LegacyAnalytics.setUser() without needing to import them.
Requirements:
- Use
declare namespace LegacyAnalytics. - Define a
trackEventfunction that accepts astringand aRecord<string, any>. - Define a
setUserfunction that accepts anumber(userId) and astring(email). - Ensure these are declared as part of the global scope.
There are no comments for now.