TypeScriptDefinitelyTyped (@types)

DefinitelyTyped (@types)

Thousands of JavaScript packages were written before TypeScript existed and ship no type information of their own. DefinitelyTyped is the community-maintained repository that fills this gap — a massive collection of type declaration packages published under the @types npm scope.

Understanding how @types packages work, when to use them, and how they interact with your tsconfig.json is essential knowledge for every TypeScript developer.

The Problem @types Solves

When you import a plain JavaScript package like lodash, TypeScript cannot infer its API. Without type information, every usage is typed as any, defeating the purpose of using TypeScript.

There are three ways a package can ship types:

  1. Bundled types — the package ships its own .d.ts files (modern packages do this)
  2. @types/* package — the community maintains declarations in DefinitelyTyped
  3. No types — you write your own declarations (covered in the next section)

Package type

Example

What to do

Bundled types

axios, zod, prisma

Nothing — types come with the install

@types package available

lodash, express, node

npm install --save-dev @types/package-name

No types anywhere

some legacy lib

Write your own .d.ts file

Installing @types Packages

@types packages are always devDependencies — they only exist at build time, never at runtime.

Bash
# Install the types for Node.js built-ins
npm install --save-dev @types/node

# Install types for Express
npm install express
npm install --save-dev @types/express

# Install types for lodash
npm install lodash
npm install --save-dev @types/lodash

# Install types for Jest
npm install --save-dev jest @types/jest
Note
TypeScript automatically discovers @types packages from your node_modules/@types folder — you do not need to reference them explicitly in most cases.
How TypeScript Resolves @types

By default, TypeScript includes all @types packages found in node_modules/@types throughout your entire directory hierarchy. This means @types/node installed in a parent node_modules folder is picked up automatically.

You can control this behaviour with two tsconfig.json options:

JSON
// tsconfig.json
{
  "compilerOptions": {
    // Only include these specific @types packages globally
    "types": ["node", "jest"],

    // Search for @types in these directories instead of node_modules
    "typeRoots": ["./node_modules/@types", "./src/types"]
  }
}
Warning
If you set the types array, TypeScript will ONLY auto-include the packages you list. All others must be explicitly referenced with a triple-slash directive or an import. Omit types entirely to keep the default behavior of including everything.
The types vs typeRoots Options

Option

What it does

When to use

types

Whitelist of @types packages to auto-include globally

Limit which globals bleed into your code

typeRoots

Directories to search for @types folders

Add a local types folder alongside node_modules/@types

(neither set)

Include ALL @types packages found in node_modules

Default — correct for most projects

Checking if a Package Has @types

The fastest way to check is to visit the TypeSearch tool at typesearch.net or simply check npm.

If a package ships its own types, its package.json will have a "types" or "typings" field:

Bash
# Check if axios bundles its own types
node -e "const p = require('axios/package.json'); console.log(p.types || p.typings)"
# Output: index.d.ts

# Check if lodash bundles types (it doesn't — use @types/lodash)
node -e "const p = require('lodash/package.json'); console.log(p.types)"
# Output: undefined

Bash
# You can also just try to install and see what the compiler says:
npm install lodash
# In your editor: import _ from 'lodash';
# TypeScript error: Could not find a declaration file for module 'lodash'.
# Try: npm i --save-dev @types/lodash
What's Inside an @types Package

An @types package is simply a folder containing .d.ts declaration files. Let's look at the structure of @types/node to understand the pattern:

Text
node_modules/@types/node/
  index.d.ts          ← main entry (re-exports all modules)
  fs.d.ts             ← types for the 'fs' module
  path.d.ts           ← types for the 'path' module
  http.d.ts           ← types for the 'http' module
  events.d.ts         ← types for the 'events' module
  package.json        ← points to index.d.ts
  ...

TS
// When you write this in your code:
import { readFileSync } from 'fs';
import { join } from 'path';

// TypeScript resolves:
// node_modules/@types/node/fs.d.ts  → readFileSync type
// node_modules/@types/node/path.d.ts → join type

const content = readFileSync(join(__dirname, 'config.json'), 'utf-8');
//    ^? string (typed correctly thanks to @types/node)
Version Alignment

@types package versions follow the version of the library they describe. The major and minor version of an @types package should match the major and minor version of the package it types.

Bash
# If you have express@4.18.x, install the 4.x types:
npm install --save-dev @types/express@^4

# If you have express@5.x, install the 5.x types:
npm install --save-dev @types/express@^5

JSON
// Good version alignment in package.json
{
  "dependencies": {
    "express": "^4.18.2",
    "lodash":  "^4.17.21"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/lodash":  "^4.14.202",
    "@types/node":    "^20.0.0"
  }
}
Tip
Run npx npm-check-updates periodically to keep both your packages and their @types counterparts in sync.
Triple-Slash References

Sometimes you need to explicitly pull in a type package that isn't auto-included. Triple-slash directives are single-line comments at the top of a file that instruct the compiler to include additional files.

TS
/// <reference types="node" />
/// <reference types="jest" />

// Now __dirname and describe() are available in this file
// even if they aren't globally included by tsconfig.json

describe('file utilities', () => {
  it('resolves paths', () => {
    const p = require('path').join(__dirname, 'fixtures');
    expect(p).toContain('fixtures');
  });
});
Note
Triple-slash references are mostly needed in .d.ts files and library code. In regular application code, simply configure tsconfig.json and let automatic resolution handle it.
When @types Conflicts With Bundled Types

Occasionally a package ships its own types but a stale @types package is also installed, causing duplicate or conflicting declarations. The fix is to remove the redundant @types package.

Bash
# axios bundles its own types since v1.0
# If you also have @types/axios installed, remove it:
npm uninstall @types/axios

TS
// Check which types file TypeScript is actually using:
// Hover over the import in your editor, or:
import axios from 'axios';
//     ^? — hover shows the resolved type file path

// Or check with tsc directly:
// npx tsc --traceResolution 2>&1 | grep axios
Patching @types With Your Own Declarations

When an @types package is missing a property or has an incorrect type, you can patch it using module augmentation instead of waiting for a pull request to be merged.

TS
// src/types/lodash.d.ts
// Imagine @types/lodash is missing a function we need to type
import 'lodash';

declare module 'lodash' {
  interface LoDashStatic {
    // Add the missing method signature
    sumBy<T>(collection: T[], iteratee: (item: T) => number): number;
  }
}

// Now TypeScript accepts this without errors:
import _ from 'lodash';
const total = _.sumBy(orders, (o) => o.price);
The DefinitelyTyped Repository

The DefinitelyTyped repository lives at github.com/DefinitelyTyped/DefinitelyTyped. It is one of the largest open-source repositories on GitHub with over 8,000 type packages.

Contributing Types

If you need to add or fix types for a package, the contribution process is:

  1. Fork the DefinitelyTyped repository on GitHub.

  2. Find or create the folder: types/package-name/index.d.ts

  3. Write your type declarations following the DefinitelyTyped conventions.

  4. Add tests in types/package-name/package-name-tests.ts

  5. Run the local test suite: cd types/package-name && npx dtslint .

  6. Open a pull request — the DT bot will automatically review and publish.

Text
types/
  lodash/
    index.d.ts         ← the declarations
    lodash-tests.ts    ← usage tests (must compile without errors)
    tsconfig.json      ← tsconfig for running the tests
    package.json       ← metadata (owners, dependencies)
Practical Example: Typing an Express App

Here's a complete setup for a typed Express application, showing all the pieces working together.

Bash
npm install express
npm install --save-dev typescript @types/node @types/express ts-node

JSON
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "moduleResolution": "node"
  },
  "include": ["src/**/*"]
}

TS
// src/app.ts
import express, { Request, Response, NextFunction } from 'express';

// All types come from @types/express
const app = express();

app.use(express.json());

// Route handler — req and res are fully typed
app.get('/users/:id', (req: Request, res: Response) => {
  const { id } = req.params; // string
  const { format } = req.query; // string | ParsedQs | string[] | ParsedQs[]

  res.json({ id, format });
});

// Error handler — four-parameter signature required by Express
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: err.message });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
Success
With @types/express installed, you get complete IntelliSense for the entire Express API — request params, query strings, response methods, middleware signatures, and more.
Summary
  • DefinitelyTyped is a community repository of type declarations for JavaScript packages that ship no types.

  • @types packages are always devDependencies — they're consumed at compile time only.

  • TypeScript auto-includes all @types packages in node_modules/@types by default.

  • Use the tsconfig "types" array to whitelist which @types are globally available.

  • Keep @types versions aligned with the major.minor version of the package they type.

  • Use module augmentation to patch incorrect or incomplete @types declarations.

  • Triple-slash directives explicitly include a types package in a single file.

  • When a package starts bundling its own types, remove the corresponding @types package to avoid conflicts.

Understanding skipLibCheck

One tsconfig flag frequently seen alongside @types usage is skipLibCheck: true. It tells TypeScript to skip type-checking inside .d.ts files — including those in node_modules/@types.

JSON
// tsconfig.json
{
  "compilerOptions": {
    "skipLibCheck": true  // skip type-checking all .d.ts files
  }
}

When to use skipLibCheck

Use it when:

  • Different @types packages declare conflicting global types (common with @types/node and browser globals)
  • A transitive dependency ships broken .d.ts files you cannot fix
  • Build speed matters and you trust your dependencies

Do not rely on it to hide bugs in your own declaration files — those should be fixed properly.

A more surgical alternative is skipDefaultLibCheck: true, which only skips TypeScript's built-in lib files, not third-party .d.ts files.

TS
// Common conflict example: @types/node vs browser globals
// @types/node declares 'global' as a namespace
// Browser code also uses 'global' patterns

// If you hit duplicate identifier errors from @types packages:
// Option 1 — skipLibCheck: true (broad)
// Option 2 — use the 'types' array to be selective:
// { "types": ["node"] }  // only include @types/node, exclude conflicting packages
Checking Type Coverage

The type-coverage tool measures what percentage of your codebase has explicit types vs implicit any. Use it to track progress when migrating a JavaScript codebase.

Bash
# Install type-coverage
npm install --save-dev type-coverage

# Run — reports percentage of typed nodes
npx type-coverage
# Coverage: 95.23% (12034/12638)

# Show all any-typed locations
npx type-coverage --detail

# Fail CI if coverage drops below a threshold
npx type-coverage --atLeast 95
Tip
Add a type-coverage check to your CI pipeline. As you install @types packages and write declarations, coverage should trend upward toward 100%.
Summary
  • DefinitelyTyped is a community repository of type declarations for JavaScript packages that ship no types.

  • @types packages are always devDependencies — they're consumed at compile time only.

  • TypeScript auto-includes all @types packages in node_modules/@types by default.

  • Use the tsconfig "types" array to whitelist which @types are globally available.

  • Keep @types versions aligned with the major.minor version of the package they type.

  • Use module augmentation to patch incorrect or incomplete @types declarations.

  • Triple-slash directives explicitly include a types package in a single file.

  • When a package starts bundling its own types, remove the corresponding @types package to avoid conflicts.

  • skipLibCheck: true skips type-checking inside .d.ts files — useful when packages conflict.

  • Track type coverage with the type-coverage tool to measure migration progress.