SOLUTION: Challenge 3 - Ambient Declaration ============================================= Challenge: Create a .d.ts file that declares a global DEBUG constant and a module type for an external library. Write code that uses both. --- SOLUTION: // types/global.d.ts (AMBIENT DECLARATION FILE) // Declare a global variable accessible from anywhere declare const DEBUG: boolean; // Declare a module (for untyped external library) declare module "untyped-math-lib" { export function complexCalculate(x: number, y: number): number; export const VERSION: string; } // Optionally, declare global namespace if needed declare global { interface Window { myGlobalConfig: { apiUrl: string; timeout: number; }; } } export {}; // Make this a module (otherwise TypeScript treats it as a script) --- // app.ts (USAGE) // 1. Use the global DEBUG constant if (DEBUG) { console.log("Debug mode is ON"); } // 2. Import from the "untyped" module (now with types!) import { complexCalculate, VERSION } from "untyped-math-lib"; console.log(`Math library v${VERSION}`); const result = complexCalculate(10, 20); console.log(`Result: ${result}`); // 3. Use the global window config if (typeof window !== "undefined") { console.log(`API URL: ${window.myGlobalConfig.apiUrl}`); console.log(`Timeout: ${window.myGlobalConfig.timeout}ms`); } --- HOW THIS WORKS: GLOBAL VARIABLE (.d.ts): declare const DEBUG: boolean; TypeScript sees this in any .d.ts file in your project and makes DEBUG available globally without importing. Useful for build-time constants. MODULE DECLARATION: declare module "untyped-math-lib" { ... } TypeScript provides types for a library that doesn't have @types. When you import from that library, TypeScript uses your ambient declaration. GLOBAL INTERFACE EXTENSION: declare global { interface Window { ... } } Adds properties to existing global objects like Window. --- SETUP: For this to work: 1. Place .d.ts in your src/ or types/ directory 2. TypeScript automatically finds .d.ts files (they're not imported) 3. Reference in tsconfig.json (usually auto-detected): { "compilerOptions": { "typeRoots": ["./node_modules/@types", "./types"] }, "include": ["src", "types"] } The .d.ts files are compiled but not executed (they're pure type info). --- REAL-WORLD EXAMPLE: Suppose you're using an old library that doesn't have types: // npm install some-old-library // types/some-old-library.d.ts declare module "some-old-library" { export interface Config { enabled: boolean; mode: "fast" | "safe"; } export function init(config: Config): void; export function run(): Promise; } // app.ts import { init, run } from "some-old-library"; const config = { enabled: true, mode: "safe" as const }; init(config); // ✅ TypeScript knows the signature const result = await run(); // ✅ Knows it returns Promise --- GLOBAL DECLARATIONS: Sometimes libraries pollute the global scope (old-school approach). Ambient declarations let you type them: // Before: no types, confusing myLibrary.helper(); // Is this function? What does it return? // After: clear types declare const myLibrary: { helper(): string; version: string; }; console.log(myLibrary.helper()); // ✅ Typed! --- TRIPLE-SLASH REFERENCES (Legacy): Old TypeScript projects use triple-slash comments to reference .d.ts: // app.ts /// Modern projects don't need this — TypeScript auto-discovers .d.ts files. --- BEST PRACTICES: 1. Keep .d.ts files in a types/ or @types/ folder 2. Name them clearly: global.d.ts, jquery.d.ts, etc. 3. For new projects, prefer DefinitelyTyped (@types packages) npm install --save-dev @types/some-library 4. Only write ambient declarations for libraries you control or old untyped libs 5. Don't over-use global declarations — prefer imports when possible --- WHEN TO USE AMBIENT DECLARATIONS: ✅ DO: - External untyped libraries (use ambient module declaration) - Build-time constants (DEBUG, APP_VERSION, etc.) - Global browser/Node APIs you've extended (window properties) ❌ DON'T: - Internal code (use proper exports/imports instead) - Types for code you write (use regular .ts files and export { type Foo }) - Polluting global scope unnecessarily (bad practice) Ambient declarations are powerful but should be used sparingly. They're mostly needed for integrating untyped external code.