19.6 DefinitelyTyped and @types Packages
Right, so you’ve written some TypeScript, you’re feeling smug about your type safety, and then you try to import lodash or react and your editor lights up like a Christmas tree with red squiggles. The library is pure JavaScript. Your brilliant type system has no idea what _.chunk() is supposed to do. This is where the magic—and the occasional dumpster fire—of DefinitelyTyped and @types packages comes in.
Think of DefinitelyTyped as the world’s largest, most chaotic, and miraculously effective group project. It’s a massive GitHub repository where volunteers write and maintain declaration files (.d.ts) for libraries that don’t ship with their own types. When you run npm install --save-dev @types/lodash, you’re not installing code from Lodash; you’re installing a package automatically published from the lodash folder within the DefinitelyTyped repository. It’s a separate entity giving your type checker the blueprint it needs to understand that library’s shape.
How @types Packages Work with npm
The system is brilliantly simple, which is why it works at all. The @types scope on npm is reserved for these type definition packages. The naming convention is straightforward: for a package named cool-lib, its types will be @types/cool-lib. The TypeScript compiler automatically looks for these. If you have @types/react installed in your node_modules, TypeScript just finds it. No need to manually reference it in your tsconfig.json. It’s like your compiler has a built-in librarian who knows to check the “Types” section for any book you check out.
# You install the JavaScript library
npm install lodash
# Then you install its type definitions
npm install --save-dev @types/lodash
Now, you can import and use lodash with full IntelliSense, as if it were written in TypeScript itself.
import * as _ from 'lodash';
const chunkedArray = _.chunk(['a', 'b', 'c', 'd'], 2);
// TypeScript knows chunkedArray is string[][]
// Try typing `_.` and enjoy the autocomplete bliss.
The Limits of the Volunteer Cavalry
Here’s the part where I have to be direct: this system is both amazing and horrifying. The quality of @types packages varies wildly. For massive projects like React or Lodash, the definitions are meticulously maintained, often by core team members or a dedicated group of experts. They’re fantastic.
For left-pad-but-for-ants, maintained by a developer named Dave who last committed in 2018? Not so much. You might find outdated types, incorrect function signatures, or entire modules that are just declared as any (the ultimate surrender flag in type design). You are relying on the generosity and spare time of strangers. Always check the “Last updated” date on the npm page for an @types package. If it’s older than your car, maybe reconsider using that library.
When You Need to Roll Your Own (The Escape Hatch)
Sometimes, the @types package doesn’t exist, or it’s catastrophically wrong. Don’t panic. You can write your own declarations. The easiest way is to create a .d.ts file somewhere in your project (I like a types/ directory) and use ambient module declarations.
Let’s say you’re using a legacy library called old-janky-lib that just drops a global function jankyFunc onto the window object. There’s no @types package. Here’s how you tell TypeScript about it:
// types/old-janky-lib.d.ts
declare module 'old-janky-lib' {
export function jankyFunc(input: string): number;
export const someConstant: boolean;
}
// Now you can import it, even though it's a global lib!
import { jankyFunc } from 'old-janky-lib';
For a global variable, you’d do this:
// types/globals.d.ts
declare global {
const myGlobal: {
danger: () => void;
};
}
// Now TS won't scream about this
myGlobal.danger();
This isn’t just a fix; it’s active documentation. You’re formally defining the contract you have with this janky library, which is often more than the library’s own docs will do.
Best Practices: Don’t Be a Schmuck
- Dev Dependencies: Always install
@typespackages as--save-dev. They’re not needed at runtime; they’re only for your compiler and editor. Shipping them to production is just wasting bytes. - Version Alignment: This is crucial. The
@types/reactpackage version18.x.xcorresponds toreactversion18.x.x. If you’re on React 17, you need@types/react@17. Installing the wrong version is a one-way ticket to type error hell. npm will usually warn you, but it’s your responsibility to get this right. - Prefer Libraries with Built-in Types: Whenever you have a choice, pick a library that bundles its own type definitions. It means the authors care about the TypeScript experience and the types are guaranteed to be in sync with the code. This is becoming the standard, and DefinitelyTyped is increasingly for legacy or massive projects that can’t easily change their release process.
- Contribute Back: If you find a bug in a type definition on DefinitelyTyped and you fix it for your own project, for the love of all that is holy, submit a PR back. This system only works because people like you eventually become people like me, who get annoyed and fix things for everyone else. It’s the circle of (type-safe) life.