Skip to content
Laviyx Learn

Try

TypeScript course menu

TypeScript Declaration Files (.d.ts) and @types Packages

Understand TypeScript declaration files: what .d.ts files contain, where library types come from (bundled types and DefinitelyTyped @types packages), installing @types/node, writing your own declarations for untyped modules and globals, augmenting existing types, and generating .d.ts files.

Advanced5 min readUpdated
On this page

TypeScript needs type information for every library you use, including ones written in plain JavaScript. That information lives in declaration files: files ending in .d.ts that contain only types, no implementation.

What a declaration file looks like#

For a small JavaScript library:

greet.js (JavaScript)
export function greet(name, excited) {
  return `Hello, ${name}${excited ? "!" : "."}`;
}
export const VERSION = "1.2.0";

the matching declaration file describes its types:

greet.d.ts (TypeScript)
export declare function greet(name: string, excited?: boolean): string;
export declare const VERSION: string;

TypeScript reads greet.d.ts to type-check code that imports greet.js. The declare keyword means "this exists at runtime somewhere else; here is its type".

Where library types come from#

When you import a package, TypeScript looks for types in this order:

  1. Bundled types: many packages ship their own .d.ts files (listed under "types" in their package.json). Nothing extra to install. Libraries written in TypeScript, such as date-fns and Zod, do this.
  2. DefinitelyTyped: a large community repository of types for JavaScript packages, published to npm under the @types/ scope. Install them as dev dependencies:
Terminal
npm install --save-dev @types/node
npm install --save-dev @types/express
  1. Nothing found: TypeScript reports an error such as:
Compiler error
error TS7016: Could not find a declaration file for module 'some-old-lib'.

On npmjs.com, packages show a "TS" badge when they include types, or a "DT" badge when types are available from DefinitelyTyped.

@types/node#

Node.js's built-in modules (fs, path, process and so on) are typed by @types/node. Install a version matching your Node.js major version. Examples in this course that import node:fs are type-checked with it:

TypeScript
import { readFile } from "node:fs/promises";
import { platform } from "node:os";

const ok: boolean = typeof readFile === "function" && typeof platform() === "string";
console.log(ok);
Output
true

Declaring an untyped module yourself#

If a package has no types at all, you can declare them in your project, for example in src/types/some-old-lib.d.ts:

some-old-lib.d.ts (TypeScript)
declare module "some-old-lib" {
  export function slugify(text: string): string;
  export default function init(options?: { debug?: boolean }): void;
}

The quickest fix is a one-line shorthand that makes the whole module any: declare module "some-old-lib";. It removes the error but also all type safety, so describe at least the parts you use when you can.

Declarations for files that are not code, such as images or CSS modules imported through a bundler, work the same way:

TypeScript
declare module "*.svg" {
  const src: string;
  export default src;
}

Declaring globals#

Scripts loaded with a <script> tag (analytics snippets, older libraries) often add global variables. Declare them so TypeScript knows they exist:

globals.d.ts (TypeScript)
declare const APP_VERSION: string;

interface Window {
  dataLayer: unknown[];
}

Because interfaces merge (Interfaces), the second declaration adds dataLayer to the built-in Window type instead of replacing it. In a file that has import or export statements, wrap such additions in declare global { ... }.

Generating declaration files#

If you publish your own TypeScript library, set "declaration": true in tsconfig.json. tsc then writes a .d.ts file next to each compiled .js file, so users of your package get types automatically.

skipLibCheck#

Type errors inside library .d.ts files (often from version mismatches between packages) are not something you can fix. The "skipLibCheck": true option skips checking all .d.ts files, which also speeds up compilation. It is common in real projects and in the setup this course uses.

Practice#

Practice exercise

A plain JavaScript file currency.js exports function format(amount, code) (returns a string) and const SUPPORTED = ["INR", "USD"]. Write a currency.d.ts that types code as one of the supported codes and makes SUPPORTED a readonly tuple.

Show solution
currency.d.ts (TypeScript)
export type CurrencyCode = "INR" | "USD";

export declare function format(amount: number, code: CurrencyCode): string;

export declare const SUPPORTED: readonly ["INR", "USD"];

Code importing from ./currency.js now gets auto-complete for "INR" and "USD", and calling format(10, "EUR") is a compile error.

Key takeaway#

Finished this lesson?