How do you type a custom property on global window in TypeScript?
Tests declaration merging and global scope augmentation in TypeScript. Strong answer: declare global in a .ts file, a .d.ts with interface Window, or scoped declare const window. Red flag: any or ts-ignore instead of interface merging.
WHAT THIS TESTS: This question probes your understanding of TypeScript global scope augmentation and declaration merging specifically for built-in DOM interfaces. The interviewer wants to know if you can correctly extend the Window interface without resorting to type escapes, and whether you understand the practical differences between ambient declaration files, module-scoped declarations, and single-file overrides. It also surfaces whether you know why interface supports merging but type does not.
A GOOD ANSWER COVERS: A strong candidate presents three solutions in order of practicality. First, create a dedicated .d.ts file such as window.d.ts and write interface Window { myAppConfig: YourConfigType } directly, because .d.ts files automatically contribute to the global scope without needing declare global. Second, inside a .ts or .tsx module, use declare global { interface Window { myAppConfig: YourConfigType } }, which relies on interface declaration merging to append properties to the existing Window definition. Third, for a local-only override that avoids global pollution, use declare const window: { myAppConfig: YourConfigType } & Window within a single file, though this must be repeated in every file that accesses the property. Crucially, you must note that interface Window works because interfaces merge, whereas using type Window would produce a duplicate identifier error since types do not merge.
COMMON WRONG ANSWERS: The biggest red flag is recommending (window as any).myAppConfig or ts-ignore, which abandons type safety entirely. Another mistake is trying to use type instead of interface inside declare global, demonstrating a gap in knowledge about declaration merging. Some candidates suggest declare global inside a .d.ts file, which is unnecessary and reveals confusion about how ambient declaration files already operate in global scope. Finally, proposing to modify node_modules or lib.dom.d.ts directly is a serious anti-pattern.
LIKELY FOLLOW-UPS: An interviewer might ask how to avoid name collisions when multiple teams attach properties to window, which you can answer by scoping everything under a single namespaced object like window.myAppConfig rather than many top-level properties. They might ask how to handle third-party scripts that mutate window, in which case the same augmentation applies but should be documented clearly. If you are building a library, expect a question about shipping the .d.ts file so consumers also get the augmented types. You might also be asked how to verify the types are picked up, which comes down to ensuring the file is included in your tsconfig or at least in the compilation context.
ONE CONCRETE EXAMPLE: Imagine your deployment pipeline injects window.APP_CONFIG with an apiUrl string and a version number. You create a file named app-config.d.ts at the project root containing interface Window { APP_CONFIG: { apiUrl: string; version: number } }. After this, window.APP_CONFIG.apiUrl is typed as string across the entire codebase without any imports. If you chose the module-scoped route instead, in a .ts file you would write declare global { interface Window { APP_CONFIG: { apiUrl: string; version: number } } } and add an empty export {} to keep the file in module mode, achieving the same result with slightly more boilerplate.
Read the original → totaltypescript.com
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.