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's really being asked
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.
The full answer
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.
The mistakes people make
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.
What usually comes next
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.
A 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.
Interview question
Which snippet globally augments Window with myProp inside a TypeScript module file?
- a.declare global { interface Window { myProp: string } }Correct
- b.interface Window { myProp: string }
- c.declare global { type Window = { myProp: string } }
- d.declare const window: Window & { myProp: string }
Why? this is the answer
Inside a module, top-level declarations are scoped locally, so you must wrap an interface in declare global to leverage declaration merging; type aliases do not merge and would conflict. Option B looks correct but only declares a module-scoped interface that shadows the global Window rather than augmenting it.
Just read this? Test yourself on what you have been reading.
Read the original → totaltypescript.com
- #typescript
- #declaration-merging
- #window
- #dom-types
- #global-scope
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on typescript — each one lists the topics its interview covers.
See open roles