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
116: Global Type Declarations
I've run into this a dozen times when working on projects that mix TypeScript with legacy JavaScript libraries. Let's say we're integrating an old analytics tool that gets loaded via a <script> tag in the HTML. It injects a global object called appAnalytics directly into the browser's window object.
The "Property does not exist" wall
I'll try to write a simple wrapper function to track an event. I'm thinking, "Easy, I'll just call it from the window," and I write this:
function trackEvent(name: string) {
window.appAnalytics.sendEvent(name);
}
Immediately, TypeScript flags appAnalytics in red. The error is a classic: Property 'appAnalytics' does not exist on type 'Window & typeof globalThis'.
Now, I know the object is there at runtime because I can see it in the browser console, but TypeScript has no idea what appAnalytics is. It only knows the standard properties that the Window interface defines.
The "Quick Fix" that I hate
If I'm in a rush, I could just cast the window to any. It's the "shut up, TypeScript" approach:
(window as any).appAnalytics.sendEvent(name);
It works. The red line vanishes. But I hate this. I've just thrown away all the benefits of using TypeScript for this specific call. I have no autocomplete for sendEvent, and if the analytics team changes the method name to track, I won't find out until the code crashes in production. We can do better.
Trying to declare it locally
I might try to just declare the interface right in my tracker.ts file. I'll try this:
interface AppAnalytics {
sendEvent: (name: string) => void;
}
declare var appAnalytics: AppAnalytics;
This actually fixes the error if I call appAnalytics.sendEvent() directly, but if I try to access it via window.appAnalytics, it still fails. Why? Because I've declared a standalone variable, but I haven't told TypeScript that the Window interface itself has been extended.
Merging into the Global Scope
This is where we need a declaration file. I'll create a file named globals.d.ts. The .d.ts extension tells TypeScript this is a definition file—it contains types, not executable code.
If I just put the interface in there, nothing happens. I need to tell TypeScript to "merge" these definitions into the global namespace. I'll use declare global:
// globals.d.ts
export {}; // This is a weird TS quirk; it forces the file to be treated as a module
declare global {
interface Window {
appAnalytics: {
sendEvent: (name: string) => void;
setUserId: (id: string) => void;
};
}
}
The export {} line is important. Without it, TypeScript treats the file as a script rather than a module, which changes how declare global behaves. By adding it, I'm explicitly saying "this is a module, and inside this module, I want to augment the global scope."
Testing the result
Now I'll go back to my tracker.ts file. I don't need to import anything from globals.d.ts because global declarations are, well, global. They are picked up by the compiler automatically as long as the file is included in the project's tsconfig.json (which it usually is by default).
function trackEvent(name: string) {
// Now this is fully typed!
// I get autocomplete for 'sendEvent' and 'setUserId'.
window.appAnalytics.sendEvent(name);
}
I've essentially performed "interface merging." TypeScript sees the original Window interface and my new definition, and it mashes them together into one single type. This is the cleanest way to handle third-party scripts or environment-specific globals without littering your business logic with as any.
📋 Practical Task
Implementing Global Config for a Theme Engine
You are working on a project where a separate CSS-in-JS library injects a global configuration object called ThemeConfig onto the window object. This object allows the app to change colors and fonts on the fly.
The Goal: Create a global type declaration so you can access window.ThemeConfig with full type safety.
Requirements:
- Create a
theme.d.tsfile. - Use
declare globalto extend theWindowinterface. - The
ThemeConfigobject should have the following properties:primaryColor: a stringisDarkMode: a booleanupdateTheme: a function that takes acolor: stringand returns nothing (void).
- Ensure the file is treated as a module so the global declaration works correctly.
- Write a small function
toggleTheme(color: string)in a separate file that callswindow.ThemeConfig.updateTheme(color)without using any type casting (noas any).
There are no comments for now.