`declare` — Types for Things That Exist Elsewhere
Ambient Types
`declare` tells TS "this exists at runtime — I just need a type for it." Used for globals, third-party libs, and config files.
What you'll learn
- Use `declare const` / `declare function` for global runtime values
- Augment the `Window` interface
- Type non-code imports like CSS or JSON
declare introduces an ambient declaration — a type
description for something that already exists at runtime. No JS
is emitted.
Global Values
You’re using a script tag that puts gtag on window. TS doesn’t
know about it. Add a declaration:
// src/types/global.d.ts
declare function gtag(command: string, target: string, params?: object): void; Now gtag("config", "G-XYZ") is typed across your project.
Augmenting Window
A cleaner pattern for browser globals — extend Window:
// src/types/global.d.ts
declare global {
interface Window {
gtag(command: string, target: string, params?: object): void;
dataLayer: unknown[];
}
}
export {}; // makes this file a module The trailing export {} is the trick — it makes the file a
module, which means declare global is needed to escape back to
the global scope.
Non-Code Imports
If you import "./styles.css" in a TS project, TS errors — it
doesn’t know what .css resolves to. Tell it:
// src/types/css.d.ts
declare module "*.css";
declare module "*.svg" {
const content: string;
export default content;
} Frameworks (Vite, Next, etc.) generally ship these declarations themselves — but you’ll add to them for custom asset types.
Environment Variables
// src/types/env.d.ts
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production";
DATABASE_URL: string;
API_KEY: string;
}
} Now process.env.DATABASE_URL is string (not string | undefined), and process.env.NODE_ENV is the literal union.
(For Vite, augment ImportMetaEnv instead.)
Up Next
Authoring a .d.ts library — what to export, how to handle
generics, common pitfalls.