Why does TypeScript need the declare keyword?
\nImagine you are building a house, but some rooms already exist — they were built by someone else (for example, a JavaScript library). You can't go in and rebuild the walls, but you need to know their layout to place the furniture correctly. This is exactly the role the declare keyword plays in TypeScript.
If you've ever worked with popular libraries (jQuery, Lodash, D3.js), you've likely encountered .d.ts files — these are the "layouts" created using declare. In this article, we'll break down what declare is, why it's needed, and how to use it in practice. Get ready — there will be lots of code and little fluff.
What is declare in TypeScript?
\ndeclare is a keyword that tells the TypeScript compiler: "Hey, this thing (variable, function, class, or module) exists, but it's defined somewhere else. Just take my word for it and let me use it in the code."
It's important to understand: declare does not generate any JavaScript code. It is purely an informational construct for the compiler. If you write declare const x: number = 5, after compilation x will disappear — it won't be in the final file.
When is it needed?
\n- \n
- Working with global variables — for example,
window,document, or variables added via CDN. \n - Typing JavaScript libraries — when the library is written in plain JS, but you want to use it with TypeScript. \n
- Creating types for configuration files — CSS modules, images, JSON files. \n
declare syntax: basic examples
\n\n1. Declaring variables
\nSuppose you have a global variable VERSION that is defined in an HTML file via <script>. TypeScript knows nothing about it, so you write:
declare const VERSION: string;\n\nconsole.log(VERSION); // OK, TypeScript knows it's a string\n\nAfter compilation, declare const VERSION: string will disappear, but console.log(VERSION) will remain.
2. Declaring functions
\nImagine you have a global analytics function trackEvent:
declare function trackEvent(eventName: string, properties?: Record<string, any>): void;\n\n// Usage\ntrackEvent("button_click", { buttonId: "submit" });\n\nThe compiler won't check the implementation — it just knows the signature.
\n\n3. Declaring classes
\nA library provides a Moment class from the global namespace:
declare class Moment {\n constructor(date: string);\n format(pattern: string): string;\n}\n\n// Usage\nconst now = new Moment("2024-01-01");\nconsole.log(now.format("YYYY-MM-DD"));\n\n\n4. Declaring modules
\nThis is the most common scenario. For example, you have CSS modules or JSON files:
\n// styles.d.ts\ndeclare module "*.css" {\n const styles: Record<string, string>;\n export default styles;\n}\n\n// images.d.ts\ndeclare module "*.png" {\n const src: string;\n export default src;\n}\n\nNow you can import CSS and images without TypeScript errors.
\n\ndeclare and .d.ts files
\nFiles with the .d.ts extension (type declarations) are the "habitat" of the declare keyword. They contain only declarations and are not compiled into JavaScript. They usually reside in a types/ folder or are shipped with the library (e.g., @types/lodash).
Example: creating types for jQuery
\n