Lesson 30 of 80
How TypeScript understands the shape of code it never actually compiled — a plain JS library, a global variable a script tag added, a .css file imported as a module. The declare keyword describes without implementing.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 2
If you `declare const __APP_VERSION__: string;` but the build tool actually never injects that global, what happens?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
declare tells TypeScript "trust me, something with this shape exists at runtime — I'm not defining it here, just describing it." It's the type-space-only half of a declaration, with no accompanying implementation:
// global.d.ts
declare const __APP_VERSION__: string; // injected by the build tool, not defined in any .ts file
// now usable anywhere in the project:
console.log(__APP_VERSION__); // ✅ type-checks, even though it's never actually declared with const/let
A .d.ts file is a file containing only these kinds of declarations — no actual implementations, no runtime code, purely type information for something that exists (or will exist) elsewhere.
// my-untyped-lib.d.ts
declare module "my-untyped-lib" {
export function doThing(x: number): string;
}
import { doThing } from "my-untyped-lib"; // now type-checked, autocomplete works
Without this, importing a library with no types (and no @types/ package available) either errors or silently becomes any everywhere it's used — a hand-written .d.ts restores real type safety for code TypeScript can't see inside.
declare module 'legacy-lib' { export function doThing(x: number): void; } — describes shape without providing implementation.
declare produces zero runtime code. It's a promise to the compiler, not a definition — if the thing you declared doesn't actually exist at runtime (wrong global name, library not actually installed), you get a runtime error the type checker had no way to catch.@types/ packages on npm are just published .d.ts files for libraries that don't ship their own types — @types/lodash is exactly the same kind of file you'd hand-write for an internal untyped library, just maintained externally.import/export, which turns it into a module and scopes its declarations to explicit exports instead of leaking into the global namespace."How would you add TypeScript types for a JavaScript library that doesn't have any?" is a genuinely common real-world scenario, and writing a .d.ts file with declare module is the concrete, correct answer — showing you've actually worked in a mixed JS/TS codebase.