@types & DefinitelyTyped
DefinitelyTyped is a massive community repository that hosts TypeScript declaration files for thousands of JavaScript libraries under the @types npm scope. Understanding how TypeScript discovers and loads these packages is essential for configuring larger projects correctly.
@types/ Packages
Installing @types/lodash places a directory of .d.ts files inside node_modules/@types/lodash/. TypeScript's compiler automatically scans node_modules/@types and includes every package it finds there — no explicit import needed for globals.
npm install --save-dev @types/node @types/lodashType Resolution: typeRoots and types
By default TypeScript includes all packages under every node_modules/@types folder in the project. Two tsconfig options narrow this:
typeRoots: an array of directories to search instead of the defaultnode_modules/@types.types: an array of package names to include; all others are ignored.
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"],
"types": ["node", "jest"]
}
}lib.d.ts
TypeScript ships with a set of built-in declaration files that describe JavaScript's standard library and browser APIs. The lib tsconfig option controls which files are included:
{ "compilerOptions": { "lib": ["ES2022", "DOM", "DOM.Iterable"] } }ES2022— modern JS globals (Array.at,Object.hasOwn, …)DOM— browser APIs (document,fetch, …)DOM.Iterable— iterable DOM collections
Node Types
For Node.js projects, install @types/node. This adds types for process, Buffer, fs, path, and every other Node built-in.
The --declaration Flag
Running tsc --declaration (or setting "declaration": true in tsconfig) generates a .d.ts file alongside each compiled .js file. This is how library authors publish types with their packages.
Creating a Simple @types Package Structure
my-types/
index.d.ts
package.json ← { "name": "@types/my-lib", "version": "1.0.0", "main": "" }strict Mode Implications
Enabling "strict": true activates a family of flags. When consuming @types packages, strict mode can surface errors in your own usage of a library that were invisible before — such as potentially-undefined return values that the library's types correctly declare as T | undefined.
Mastering type resolution saves hours of debugging mysterious "Cannot find module" or "has no exported member" errors in real projects.
Code Examples
// Source file: src/math.ts
// Run with: tsc --declaration --outDir dist
export function add(a: number, b: number): number {
return a + b;
}
export function multiply(a: number, b: number): number {
return a * b;
}
export interface Vector2 {
x: number;
y: number;
}
export function scaleVector(v: Vector2, factor: number): Vector2 {
return { x: v.x * factor, y: v.y * factor };
}
// TypeScript generates dist/math.d.ts:
// ----------------------------------------
// export declare function add(a: number, b: number): number;
// export declare function multiply(a: number, b: number): number;
// export interface Vector2 { x: number; y: number; }
// export declare function scaleVector(v: Vector2, factor: number): Vector2;
// ----------------------------------------
// Verify the functions work correctly
console.log(add(3, 4));
console.log(multiply(3, 4));
console.log(JSON.stringify(scaleVector({ x: 1, y: 2 }, 3)));The --declaration flag tells tsc to emit a .d.ts file alongside every compiled .js file. The generated file contains only declare-prefixed type signatures — no implementation code.
// After: npm install lodash && npm install --save-dev @types/lodash
// TypeScript now fully understands lodash's API
import _ from "lodash";
interface Product {
name: string;
price: number;
category: string;
}
const products: Product[] = [
{ name: "Widget", price: 9.99, category: "tools" },
{ name: "Gadget", price: 24.99, category: "electronics" },
{ name: "Thingamajig", price: 4.99, category: "tools" },
{ name: "Doohickey", price: 14.99, category: "electronics" },
];
// _.groupBy, _.mapValues, _.sortBy — all fully typed
const grouped = _.groupBy(products, "category");
const cheapest = _.minBy(products, "price");
const sorted = _.sortBy(products, ["category", "price"]);
console.log("Categories:", Object.keys(grouped));
console.log("Cheapest:", cheapest?.name);
console.log("First sorted:", sorted[0].name);@types/lodash ships complete signatures for every lodash function. TypeScript infers the return type of _.groupBy as Dictionary<Product[]> and _.minBy as Product | undefined, giving full type safety.
// tsconfig.json (illustrative — not executable TypeScript)
// {
// "compilerOptions": {
// "typeRoots": [
// "./node_modules/@types",
// "./src/custom-types"
// ],
// "types": ["node", "jest"],
// "lib": ["ES2022"],
// "strict": true
// }
// }
// With the above config TypeScript only auto-includes @types/node
// and @types/jest — all other @types packages are ignored unless
// explicitly imported.
// src/custom-types/env.d.ts would live here for project-specific globals.
// Demonstration: type narrowing that strict mode enables
function processValue(val: string | null | undefined): string {
// Without strict, TypeScript might not warn about missing null check
if (val == null) {
return "(empty)";
}
return val.toUpperCase(); // safe because null/undefined excluded above
}
console.log(processValue("hello"));
console.log(processValue(null));
console.log(processValue(undefined));
// strict mode also enables:
// - strictNullChecks
// - noImplicitAny
// - strictFunctionTypes
// - strictPropertyInitialization
// - and more
console.log("strict mode catches type errors at compile time");The types array restricts which @types packages are automatically included globally. typeRoots adds custom type directories. Together they give precise control over what type definitions the compiler loads.
Quick Quiz
1. By default, TypeScript automatically includes types from which directory?
2. What does the tsconfig "types" array do?
3. Which tsconfig flag causes tsc to emit .d.ts files alongside compiled JavaScript?
4. Which lib value adds browser API types like document and fetch to a TypeScript project?
Was this lesson helpful?