Working with Third-Party Types
How TypeScript finds types for npm packages, and what to do when a package doesn't ship them.
2 min read
Real TypeScript projects depend on npm packages, and most of those packages were originally written in plain JavaScript. This lesson covers how TypeScript finds — or fails to find — type information for a dependency, and what to do about it.
Packages that ship their own types
Many modern packages include a .d.ts declaration file alongside their JavaScript, describing every exported function, class, and type without containing any actual implementation code:
// Roughly what a .d.ts file looks like -- types only, no logic
export function debounce<T extends (...args: any[]) => void>(
fn: T,
delay: number
): T;When a package includes this (referenced via a types or typings field in its package.json), import from it is fully typed with zero extra setup — TypeScript finds the .d.ts file automatically.
Packages without types: @types packages
For popular packages that don't ship their own types, the community maintains type definitions separately, published under the @types npm scope as part of the DefinitelyTyped project:
npm install --save-dev @types/lodashimport _ from "lodash";
_.chunk([1, 2, 3, 4], 2); // fully typed, even though lodash itself ships no typesTypeScript automatically checks for a matching @types/<package-name> package and uses it if present — no import changes needed on your end beyond installing it.
When no types exist at all
For an obscure or internal package with neither its own types nor a @types package, TypeScript refuses to import it under strict mode:
import weirdLib from "weird-lib"; // Could not find a declaration file for module 'weird-lib'Two options at that point:
// Option 1: declare it as `any` yourself, in a .d.ts file in your project
declare module "weird-lib";// Option 2: a quick, local escape hatch (less ideal, but sometimes necessary)
// @ts-ignore
import weirdLib from "weird-lib";A declare module statement (typically placed in a file like src/types/weird-lib.d.ts) tells TypeScript "trust me, this module exists" — everything imported from it becomes any unless you write out its actual shape yourself, which is worth doing incrementally for anything used heavily.
Writing your own minimal declaration
For a small library you use in a few specific ways, writing just enough of a declaration to type those specific calls is often worth the ten minutes it takes:
// src/types/weird-lib.d.ts
declare module "weird-lib" {
export function doThing(input: string): number;
}Once declared this way, doThing is fully typed everywhere it's imported, even though the actual weird-lib package itself remains plain, untyped JavaScript.
The final lesson in this course pulls everything together into a set of practical habits for keeping a real TypeScript codebase strict, readable, and genuinely type-safe.