Packages That Ship TypeScript Types vs 2026
TL;DR
In 2026, 78% of the top 1000 npm packages ship their own TypeScript types, up from 45% in 2020. Bundled types are always better: they're versioned with the package, maintained by the same team, and never lag behind breaking changes. DefinitelyTyped (@types/) still has ~10,000 packages but should be considered a legacy fallback — for new projects, prioritize packages with built-in types.
Key Takeaways
- 78% of top 1000 packages now ship built-in TypeScript types (2026)
@types/lag problem: DefinitelyTyped types can be months behind the actual package- Version mismatch pain:
package@2.0with@types/package@1.9→ silent errors "types"field in package.json — how to check for built-in types- TypeScript-first packages tend to have better documentation and fewer runtime surprises
Built-In Types vs DefinitelyTyped
// Built-in types (best): types are IN the package
// package.json:
{
"name": "fastify",
"version": "4.24.0",
"types": "fastify.d.ts" // ← types ship with package
// OR:
"exports": {
".": {
"types": "./index.d.ts"
}
}
}
// DefinitelyTyped (@types/): types are a SEPARATE package
// npm install express
// npm install -D @types/express ← separate install, separate version
// The problem: express@4.18.2 + @types/express@4.17.21
// Package: 4.18.2 | Types: 4.17.21 → 1 minor version behind
// Can cause: missing types for new methods, wrong signatures
How to Check Which Type System a Package Uses
# Method 1: npm page
# npmjs.com/package/fastify — will show TypeScript badge if types bundled
# Method 2: Check package.json
npm view fastify --json | jq '.types, .typings, .exports'
# If any of these return a .d.ts path: built-in types ✅
# If all null: needs @types/ or has no types
# Method 3: Check package contents
npm pack fastify --dry-run | grep .d.ts
# Shows .d.ts files in the package
# Method 4: PkgPulse
# Health score includes TypeScript support indicator
# pkgpulse.com/compare/fastify-vs-express
# Quick check via TypeScript:
import fastify from 'fastify';
// If TypeScript finds types without @types/: built-in ✅
// If TypeScript errors: needs @types/ or no types available
Major Packages: Built-In Types ✅
// These packages ship their own types — no @types/ needed:
// Web frameworks:
import Fastify from 'fastify'; // fastify ✅
import { Hono } from 'hono'; // hono ✅
// import Koa from 'koa'; // ❌ needs @types/koa
// State management:
import { create } from 'zustand'; // zustand ✅
import { atom } from 'jotai'; // jotai ✅
import { proxy } from 'valtio'; // valtio ✅
// Data fetching:
import { useQuery } from '@tanstack/react-query'; // ✅
import { ofetch } from 'ofetch'; // ofetch ✅
// Validation:
import { z } from 'zod'; // zod ✅
import * as v from 'valibot'; // valibot ✅
// ORM:
import { drizzle } from 'drizzle-orm'; // drizzle-orm ✅
import { PrismaClient } from '@prisma/client'; // prisma (generated) ✅
// Build tools (cli only, but types for config):
// vite.config.ts: built-in types for defineConfig ✅
// Test runners:
import { describe, it, expect } from 'vitest'; // vitest ✅
// Jest: needs @types/jest ❌ (or jest/globals if configured)
// Logging:
import pino from 'pino'; // pino ✅
// Date:
import dayjs from 'dayjs'; // dayjs ✅
import { format } from 'date-fns'; // date-fns ✅
Packages That Still Need @types/
// DefinitelyTyped still needed for:
// Old guard (pre-TypeScript era):
npm install -D @types/express // express (old but stable)
npm install -D @types/node // Node.js built-ins (runtime types)
npm install -D @types/react // react (types are separate by design)
npm install -D @types/react-dom
// express: has been promised built-in types since v5, not yet shipped
// react: intentional separation from @types/react, maintained by community
// Testing:
npm install -D @types/jest // jest (if not using jest/globals)
npm install -D @types/mocha // mocha
// Utility libraries:
npm install -D @types/lodash // lodash
npm install -D @types/uuid // uuid (v9+ has built-in types actually)
// Note: @types/node is special — it's always needed for Node.js APIs
// even if you're using packages with built-in types
The Version Mismatch Problem
// Real example: express breaking types between versions
// package.json:
{
"dependencies": {
"express": "^4.18.0" // Installed: 4.18.2
},
"devDependencies": {
"@types/express": "^4.17.0" // Installed: 4.17.21
}
}
// Problem: express added new methods/changed signatures in 4.18
// @types/express@4.17.21 doesn't know about them
// TypeScript says: no error ✅
// Runtime: behavior differs from types ❌
// This happened with:
// - Node.js fetch types vs @types/node
// - react-router-dom between major versions
// - Several AWS SDK types
// Solution: lock @types/ versions tightly
// "@types/express": "4.17.21" ← exact, not ^
// Or better: pick packages with built-in types
TypeScript Adoption in npm: The Data
TypeScript type support across npm packages (2026):
Top 100 packages: 95% have types (bundled or @types/)
Top 1,000 packages: 89% have types
Top 10,000 packages: 78% have types
All packages: ~40% have types (long tail is JS-only)
Of packages WITH types:
- Bundled types: 78% (up from 45% in 2020)
- Only @types/: 22% (declining as packages migrate)
Why bundled types are growing:
1. TypeScript itself grew: ~68% of JS devs use TS (State of JS 2025)
2. Package authors feel pressure to support TypeScript properly
3. npm shows TS badge prominently → drives competitive behavior
4. tRPC/Zod pattern: end-to-end types REQUIRE bundled types to work
How to Check Type Quality (Not Just Presence)
// Having types isn't enough — type QUALITY matters
// Red flags in @types/ packages:
// ❌ Everything typed as `any`
declare function doThing(options: any): any;
// ❌ Missing generics
declare function useQuery(key: string, fn: Function): any;
// Should be:
declare function useQuery<T>(key: string, fn: () => Promise<T>): { data: T | undefined };
// ❌ Types in wrong export location (export default vs export =)
// Causes: "Module has no exported member" errors
// ✅ Good type quality signals:
// - Generics used throughout
// - Return types inferred from input types
// - Utility types used correctly (Partial, Required, Pick, Record)
// - TypeScript strict mode compatibility
// - Types tested in the package's own test suite (dts-jest or tsd)
tsconfig Best Practices with @types/
// tsconfig.json
{
"compilerOptions": {
// Only include @types/ packages you explicitly need
// Without this, TypeScript includes ALL installed @types/ automatically
"types": ["node", "jest"],
// Or: don't specify "types" and let TypeScript auto-discover
// Better for most projects
// Check type version compatibility:
"skipLibCheck": true, // Skip type checking of declaration files
// Use this if you hit type conflicts between @types/ packages
// Not ideal but sometimes necessary with complex dep trees
// Better alternative:
"skipLibCheck": false, // Keep it on, fix the root cause
}
}
Migrating Off DefinitelyTyped
# Check if a package now ships its own types (many did since 2022):
# uuid migrated in v9:
# Before: npm install uuid @types/uuid
# After (uuid v9+): npm install uuid ← no @types/ needed
npm view uuid --json | jq '.types'
# Returns: "dist/cjs/index.d.ts" ✅
# If a package you use added built-in types:
npm uninstall @types/package-name
npm install package-name@latest
# TypeScript should pick up the bundled types automatically
The Future: TypeScript-Native Packages
// Trend: packages written in TypeScript from day 1
// No .js + .d.ts dance — TypeScript source → distribution
// Tools that compile packages:
// tsup: builds both CJS and ESM with types from a single TypeScript source
// unbuild: used by Nuxt ecosystem packages
// pkgroll: Rollup-based, type-aware
// package.json pattern for TypeScript-native packages:
{
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"require": "./dist/index.cjs",
"import": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}
}
}
// This is the modern standard — CJS + ESM + types, single package
The Economic Case for TypeScript-First Package Selection
Choosing packages that ship their own TypeScript types is not just about technical correctness — it has measurable economic implications for development velocity. When a package's types lag behind the implementation, TypeScript's promise of catching errors at compile time breaks down. Developers encounter runtime errors in production for conditions that TypeScript should have caught, then spend time diagnosing whether the bug is in their code or in the type definitions. The time spent on this class of problem is invisible in typical project tracking but real in its impact on delivery speed.
The trend toward bundled types is driven partly by package authors experiencing this pain on the other side. When users report "TypeScript says this is wrong but it works" or "TypeScript is OK with this but it crashes at runtime," the root cause is often a type definition lag. Authors who maintain their own types have the incentive structure to keep them accurate — a type inaccuracy in your own package reflects poorly on you and generates issues in your repository. DefinitelyTyped type authors have no such accountability — a type inaccuracy in someone else's package, for which they volunteered to write types, reflects poorly on no one in particular.
The 78% bundled types figure among top packages in 2026 represents a meaningful shift from 2020's 45%. The remaining 22% that still rely on DefinitelyTyped are mostly packages that predate TypeScript's mainstream adoption and have not yet prioritized migrating their type definitions into the main package repository.
How DefinitelyTyped Actually Works
DefinitelyTyped (@types/) is a massive community repository of TypeScript declaration files for JavaScript packages that don't ship their own types. The repository contains over 10,000 package type definitions, maintained by volunteers. When you run npm install -D @types/express, you are installing a package generated from the types/express directory in the DefinitelyTyped repository on GitHub. The @types/ packages are published automatically by the DefinitelyTyped team's tooling when type definitions are merged.
The governance model explains both its strength and weakness. The strength: anyone can submit types for any package, and the community reviews and improves them over time. Packages like @types/node and @types/react have hundreds of contributors and are genuinely excellent type definitions. The weakness: type definitions are only as current as the last volunteer who updated them. When express releases 5.x with a changed API, the @types/express package may lag by weeks or months until someone volunteers to update it. During this gap, TypeScript compiles without errors on code that will fail at runtime.
The Declaration File Quality Spectrum
Having TypeScript types in a package does not guarantee those types are useful. Declaration file quality ranges from comprehensive and precise to technically present but practically useless. The worst case is a package whose entire public API is typed as any — TypeScript accepts all usage but provides no safety. This pattern appears in older packages that added types as an afterthought, adding just enough declaration to silence the "could not find a declaration file" error without providing real type safety.
The quality signals to look for are: whether the library uses generics to preserve type information across calls (a useQuery<T> that returns typed T | undefined data is better than any), whether discriminated unions are used for variant results (a result type with { status: 'success', data: T } | { status: 'error', error: Error } is better than a union of optional fields), and whether the types include JSDoc comments that appear in editor hover text. These quality markers distinguish type definitions that actively help you write correct code from type definitions that merely suppress TypeScript errors.
Module Resolution and the exports Field
The "types" field in package.json was the original mechanism for declaring the TypeScript entry point, but the modern standard is the "exports" field with condition-based resolution. The "exports" field allows a package to specify different entry points for different importers: CommonJS consumers get the CJS build, ESM consumers get the ESM build, and TypeScript consumers get the corresponding .d.ts file for each. A package that ships both CJS and ESM should have an "exports" map that routes TypeScript to the correct .d.ts for the module format being used.
This matters because CJS and ESM have different TypeScript handling. A .d.ts file generated for a CJS module uses export = syntax, which is incompatible with ESM import style in TypeScript's strict mode. Packages that ship dual CJS/ESM builds with a single shared .d.ts file can cause TypeScript errors in ESM-consuming projects. The fix is for packages to ship separate .d.ts files for each module format — index.cjs.d.ts and index.mjs.d.ts — and reference them from the "exports" map conditions. This is what the tsup build tool generates by default, which is one reason tsup has become the standard build tool for TypeScript library authors.
The @types/node Special Case
@types/node deserves special attention because it is unlike other DefinitelyTyped packages. Node.js is the runtime environment, not a library, and @types/node provides types for all of Node.js's built-in modules: fs, path, http, crypto, stream, worker_threads, and dozens more. Even packages that ship their own TypeScript types still require @types/node if they use Node.js APIs in type-visible ways — for instance, a logging library that returns a stream.Writable needs @types/node installed in the consuming project for the type to resolve correctly.
@types/node is versioned separately from Node.js itself, with packages like @types/node@22 corresponding to Node.js 22's API surface. Specifying the Node.js version-specific @types/node in your devDependencies is important for projects that need to maintain compatibility with older Node.js versions — using @types/node@22 types in a project that must run on Node.js 18 can introduce type definitions for APIs that don't exist in the runtime, leading to runtime failures that TypeScript did not catch.
Authoring TypeScript Packages: The Modern Standard
For developers publishing npm packages in 2026, the expected approach for TypeScript support is clear. Write the package source in TypeScript, use tsup or unbuild to compile both CJS and ESM outputs with corresponding .d.ts files, and configure the package.json "exports" field to route consumers to the correct output based on their module format and TypeScript needs. The resulting package structure — dual format outputs with accurate types — satisfies TypeScript in strict mode, works in both CJS and ESM consumers, and supports bundlers that need the ESM output for tree-shaking.
The "typesVersions" field in package.json is a legacy mechanism for providing different types to different TypeScript versions. Modern packages can generally ignore it, as the "exports" field with "types" conditions handles TypeScript 4.7+ correctly. Packages that need to support TypeScript 4.6 and earlier (for enterprise consumers that move slowly) still need "typesVersions" as a fallback.
Practical Evaluation Checklist
When evaluating a package for TypeScript production use, run through this sequence. First, check if the package ships bundled types by examining its package.json on npm — look for a "types" or "typings" field, or a "types" condition in the "exports" map. Second, install the package and open its entry point in your editor — if types resolve without installing a separate @types/ package, it has bundled types. Third, hover over the package's main export in your editor: if you see any everywhere, the types are placeholder-quality. If you see precise generic types with proper documentation comments, the types are production-quality.
Fourth, check whether the package's TypeScript types are tested as part of its CI pipeline. Packages that use tsd or dtslint to test their type definitions catch type regressions before they reach npm. This is visible in the repository's CI configuration — a test:types script or a dts-jest.config.ts file indicates the maintainers treat types as a first-class feature rather than an afterthought. These packages are less likely to introduce silent breaking changes to their type definitions between minor versions.
Compare TypeScript support and package health on PkgPulse.
See also: Typescript vs Jsdoc and TypeScript Adoption Rate Among Top npm Packages, Best TypeScript-First Build Tools 2026.
See the live comparison
View typescript vs. jsdoc on PkgPulse →