@types Packages and When DefinitelyTyped Is Wrong
Fix the missing declaration error, install the types package and recognise the three ways those types go wrong. After this, you know when to trust a declaration and how to correct one in a single place.
Course content
Sign in to save your progress.
-
Generics Without the Fog
-
Generic Functions That Keep the Input Type
-
Generic Types for Lists, Results and Caches
-
-
-
Types That Transform Other Types
-
Utility Types: Partial, Pick, Omit and Record
-
Mapped Types You Can Actually Read
-
-
-
Working With Code You Didn't Write
-
@types Packages and When DefinitelyTyped Is Wrong
-
Typing fetch JSON Without Lying to Yourself
-
-
-
Classes and Modules in TypeScript
-
Classes With public, private and protected
-
Modules, Exports and Avoiding Circular Types
-
-
-
Strictness That Pays Off
-
unknown vs any and Safe Parsing
-
Discriminated Unions for API Responses That Vary
-
-
- Final Check: TypeScript Intermediate
Skip ahead: If you already run npm install --save-dev @types/... and know it is separate from the library, skip to "When the types are wrong", then continue.
TypeScript can only check code it has types for, and most of the npm registry was written in plain JavaScript. A library has types in one of two ways. Either it ships its own declaration files, or a volunteer project called DefinitelyTyped writes them and publishes them as a separate package named @types/<library>. This lesson is about the second group, because that is where the surprises are. You install a second package, and now the types and the code can disagree.
The examples use lodash, a utility library written in JavaScript. Open a project that has TypeScript installed, and tell Node that your files are modules.
npm pkg set type=module
npm install lodash
added 1 package, and audited 4 packages in 1s
found 0 vulnerabilities
What the error looks like
import _ from "lodash";
const rows = _.chunk([1, 2, 3, 4, 5], 2);
console.log(rows);
chunks.ts(1,15): error TS7016: Could not find a declaration file for module 'lodash'. '/path/to/project/node_modules/lodash/lodash.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/lodash` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash';`
The path in the message is your own project folder. TS7016 means the compiler found the JavaScript but nothing that describes it. It is an error in strict mode because otherwise _ would quietly be any and every call on it would go unchecked.
Step 1: see whether the library already has types
Newer libraries often ship their own. Ask npm before you install anything.
npm view lodash types
When the package ships types, this usually prints the path of the declaration file. For lodash it prints nothing, so look for an @types package. The npm page of a package usually shows a small "DT" badge next to its name when types exist on DefinitelyTyped.
Step 2: a quick way out when nothing exists
If no @types package exists, the error message already tells you the fallback. Make a file named declarations.d.ts and put one line in it.
declare module "lodash";
npx tsc --noEmit chunks.ts declarations.d.ts
That prints nothing, so the error is gone. It is gone because everything from lodash is now any. You silenced the compiler. You did not type the library. Treat it as a placeholder and write real declarations for the few functions you use when you have time.
Step 3: install the types
rm declarations.d.ts
npm install --save-dev @types/lodash
added 1 package, and audited 5 packages in 1s
found 0 vulnerabilities
--save-dev is correct here. Your running program never loads the types, and only the compiler reads them. Now the same file compiles, and the checks are real.
import _ from "lodash";
const rows = _.chunk([1, 2, 3, 4, 5], 2);
console.log(rows);
[ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]
import _ from "lodash";
const pairs = _.chunk([1, 2, 3], "2");
const words = _.chunk(["a", "b"], 1);
const first: string = words[0];
chunks_bad.ts(3,34): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
chunks_bad.ts(5,7): error TS2322: Type 'string[]' is not assignable to type 'string'.
The first error is the second argument, which has to be a number. The second is the return type: chunk gives back an array of arrays, so words[0] is a string[]. None of that was visible before the install.
When the types are wrong
Remember who writes these files. The library authors write the code. Someone else writes the types, usually by reading the documentation. Three things go wrong, and none of them announces itself:
- The types lag. The library adds a function and the types do not know it yet. The call is a compile error and a perfectly good call at run time.
- The versions drift. When I ran the examples above, lodash was 4.18.1 and
@types/lodashwas 4.17.26. Close is normal. A big gap is a warning. Check that your@typesversion belongs to the same major version of the library. - A type promises more than the code does. This is the dangerous one, because the compiler is certain and wrong.
Here is the third one in miniature. I wrote a tiny JavaScript file and a declaration file for it, so I am not picking on a real package.
const rates = { USD: 1, PHP: 56.2 };
export function findRate(code) {
return rates[code];
}
export function findRate(code: string): number;
The declaration says number. The code returns undefined for a currency it does not know.
import { findRate } from "./vendor/rates.js";
const rate = findRate("EUR");
console.log(100 * rate);
try {
console.log(rate.toFixed(2));
} catch (error) {
console.log((error as Error).message);
}
NaN
Cannot read properties of undefined (reading 'toFixed')
It compiles with no complaint, 100 * rate is NaN, and the next line throws. You cannot blame the compiler: it believed the declaration. The fix is to stop trusting the declaration in one place and say what you really know. Wrap the call in a function of your own with an honest return type.
import { findRate } from "./vendor/rates.js";
function safeRate(code: string): number | undefined {
return findRate(code);
}
const rate = safeRate("EUR");
console.log(rate.toFixed(2));
convert_safe.ts(8,13): error TS18048: 'rate' is possibly 'undefined'.
A number fits into number | undefined, so the wrapper compiles. Everything that calls safeRate now has to deal with the missing case. A tempting shortcut is const rate: number | undefined = findRate("EUR"), but it does not work: the compiler narrows the variable to number as soon as you assign it. The wrapper function is the version that holds.
When a method is missing from the types
Some code adds methods at run time, and no declaration file can know. lodash's mixin is an example. You tell the compiler with a module augmentation.
import _ from "lodash";
_.mixin({ shout: (text: string) => text.toUpperCase() + "!" });
console.log(_.shout("done"));
mixin.ts(4,15): error TS2339: Property 'shout' does not exist on type 'LoDashStatic'.
import _ from "lodash";
declare module "lodash" {
interface LoDashStatic {
shout(text: string): string;
}
}
_.mixin({ shout: (text: string) => text.toUpperCase() + "!" });
console.log(_.shout("done"));
DONE!
The declare module "lodash" block merges your extra method into the library's own LoDashStatic interface. Keep a block like this in one file named for the library, so the next developer finds it.
⚠ Warning: declare module "name"; with no body makes the whole library any. It is fine as a first step. Do not leave it there.
Common mistakes
- Installing
@types/xwithout installingx. The types are not the library. - Putting
@typespackages independencies. They belong indevDependencies. - Fixing a wrong type with
as anyat every call. Fix it in one place, with your own declaration or wrapper. - Believing that a type error from a library is always your fault. Sometimes the declaration is wrong.
💡 Tip: When the compiler and the library disagree, read the library's own source or run the call in plain Node first. That tells you which side is wrong.
If you skip this, you will install @types packages on autopilot and trust them without question, and one day a declaration will promise a number where the code returns nothing. Which library in your project do you trust only because the compiler agrees with it?
About
Fix the missing declaration error, install the types package and recognise the three ways those types go wrong. After this, you know when to trust a declaration and how to correct one in a single place.
- Duration
- 10 min
- Last update
- 10/22/2026
Comments (0)
Sign in to commentNo comments yet.