A declaration file describes the types of JavaScript that exists elsewhere. It has the
extension .d.ts, .d.mts or .d.cts, holds no code, and
produces no output. Compiled libraries ship one beside each JavaScript file
(Handbook:
Type Declarations and
Declaration
Files).
What a declaration file may contain
Every top-level item must start with declare or export, or be
an import, interface or type. Statements are not
allowed. Function and method bodies are not
allowed. A const may have a literal initialiser, but no other expression.
// lib.d.ts
declare function greet(name: string): void;
declare function greet(names: string[]): void; // overloads list signatures only
declare const VERSION = "1.0"; // a literal initialiser is allowed
declare let count: number;
declare class Client {
url: string;
constructor(url: string);
send(body: string): Promise<void>;
private token; // private members are named with no type
}
declare namespace Client {
interface Options { timeout?: number }
}
interface Point { x: number; y: number }
type Id = string | number;
function bare(): void; // error: must start with declare or export
declare function body() {} // error: no bodies
declare const o = { a: 1 }; // error: only a literal
The same rules apply to an ambient block of declare in any
.ts file. Inside such a block, declare is implied.
A declaration file is a script or a module by the same test as other files: a top-level
import or export makes it a module. In a module declaration file
every declaration is exported unless the file contains export {},
which turns off this automatic export.
declare
declare states that something exists at run time without creating it.
Applied in a .ts file, it is erased, so it is how a program describes a value
provided by the page or the runtime.
declare const API_URL: string; // provided by the page
fetch(API_URL);
declare function ga(event: string): void;
declare states existence and generates no codeGenerating declarations
The compiler writes declaration files from source when declaration is on.
Inferred types are written out, overload signatures are kept and implementations are
removed.
export declare class P {
x: number;
private readonly w;
constructor(x: number, w?: number);
}
declare const _default: number;
export default _default;
| Option | Effect |
|---|---|
declaration | Writes the declaration files |
declarationDir | Where they go |
declarationMap | A map back to the original source |
emitDeclarationOnly | Writes only these, not JavaScript |
stripInternal | Leaves out members marked @internal |
isolatedDeclarations | Requires annotations on exports, so that other tools can write the file with no type checking (5.5) |
Augmentation
A module can add to another module, or to the global scope.
Module augmentation
In a module file, declare module "name" merges declarations into a module
that already exists, and existing interfaces can be extended. The Handbook notes that a
default export cannot be augmented.
import { Observable } from "./observable.js";
declare module "./observable.js" {
interface Observable<T> {
map<U>(f: (x: T) => U): Observable<U>;
}
}
Observable.prototype.map = function (f) { /* … */ };
Global augmentation
Inside a module, declare global adds to the global scope. A script’s
top-level declarations are already global.
export {};
declare global {
var appVersion: string;
interface Array<T> {
first(): T | undefined;
}
}
globalThis.appVersion = "1";
export {} makes the file a moduleThe standard library
The types of the built-in objects are declaration files shipped with the compiler,
named such as lib.es2025.d.ts and lib.dom.d.ts. By default the
compiler loads the set that matches target, and lib replaces that
set. noLib loads none.
Since 6.0, packages under node_modules/@types are no longer loaded
automatically, as types defaults to []. List what the project
uses, such as ["node"], or use ["*"] for the old behaviour.
Packages named in an import are still found. skipLibCheck skips checking
declaration files, and skipDefaultLibCheck only those shipped with the
compiler.
Publishing types
A package written in TypeScript ships its generated declarations and points to them
from package.json
(Handbook:
Publishing).
{
"name": "awesome",
"main": "./lib/main.js",
"types": "./lib/main.d.ts",
"exports": {
".": {
"types": "./lib/main.d.ts",
"default": "./lib/main.js"
}
}
}
types is the older field. A types condition in exports applies when that map is readIf the public types use another package’s types, that package belongs in
dependencies. typesVersions redirects to different files by
compiler version, and the first matching range is used, so the newest should come
first. Once exports is read, typesVersions is ignored.
DefinitelyTyped
Types for a package that does not include them are usually in the
DefinitelyTyped
repository, which is published to npm under the @types scope. They are
installed as packages, such as @types/node, and are found automatically for
imports, as well as through the types option for global declarations.