TypeScript

TS7016: Could not find a declaration file for module (fix)

TS7016 fires when you import an untyped JavaScript module under noImplicitAny. The right fix is usually `npm i -D @types/X` — but sometimes the package already ships types, and sometimes you need to write a one-line declaration.

Mahdi Ben RhoumaMahdi Ben Rhouma
Updated
8 min read

You import a perfectly normal npm package and TypeScript stops you:

text
Could not find a declaration file for module 'some-lib'.
'.../node_modules/some-lib/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/some-lib` if it exists or add a new
  declaration (.d.ts) file containing `declare module 'some-lib';`

This is TS7016. TypeScript even prints its own two suggested fixes. This guide explains when each is right, plus the case the message doesn't mention: the package may already ship types.

Why it happens#

TS7016 fires when all three are true:

  1. You import a module that resolves to a plain JavaScript file with no bundled .d.ts, and
  2. there is no @types/X package installed, and
  3. noImplicitAny is on (it is, under strict).

Without type information the module would silently become any. The noImplicitAny docs put it plainly: with the flag on, "TypeScript will issue an error whenever it would have inferred any." So TS7016 is the protection working — not a broken install.

Fix 1 (preferred) — Install the DefinitelyTyped types#

bash
npm install --save-dev @types/some-lib

How to know it exists: DefinitelyTyped publishes type packages to npm under the @types scope with the same name as the underlying package (react → @types/react). Per the handbook, "TypeScript automatically finds type definitions under node_modules/@types, so there's no other step needed." If the install errors with 404, no community types exist — go to Fix 3.

Fix 2 — Check if the package already ships types#

Sometimes TS7016 is misdiagnosed: the package does bundle types and you're chasing a phantom. Look in the dependency's package.json for a types (or its synonym typings) field:

json
{
  "main": "./lib/main.js",
  "types": "./lib/main.d.ts"
}

If a package bundles types but you still see TS7016, suspect a stale install, a typesVersions mismatch for your TS version, or an exports map that doesn't expose the .d.ts. The last one has its own wording. We reproduced it on 1 October 2026 with TypeScript 5.9.3: a package whose top-level types field points to ./types/index.d.ts but whose exports map only lists ./dist/index.js:

text
error TS7016: Could not find a declaration file for module 'libx'. '/c/node_modules/libx/dist/index.js' implicitly has an 'any' type.
  There are types at '/c/node_modules/libx/types/index.d.ts', but this result could not be resolved when respecting package.json "exports". The 'libx' library may need to update its package.json or typings.

It appeared under moduleResolution nodenext and bundler, which read exports and ignore the top-level types field once exports is present. Switching back to the legacy node10 resolution silenced it on TypeScript 5.9, but TypeScript 7 rejects that setting outright (error TS5108: Option 'moduleResolution=node10' has been removed), so it is not a fix to rely on. The real fix belongs to the library: a types condition inside exports (".": { "types": "./types/index.d.ts", "default": "./dist/index.js" }) made the same import compile cleanly in our test. Until the library ships that, use a local declaration (Fix 3) and report the issue upstream.

A related variant: the mismatch appears right after bumping TypeScript itself — npm install typescript@latest breaks the build with Cannot find module '@types/node' or Property 'auth' does not exist on SupabaseClient. That's not a missing-types issue but a module/moduleResolution drift between the compiler and @types/node; the dedicated guide to aligning TypeScript and @types/node covers realigning both in one pass.

Fix 3 — Write a local declaration#

When neither @types/X nor bundled types exist, declare the module yourself. Create a file like src/types/some-lib.d.ts. The minimal "make it any" form (TypeScript's own second suggestion):

typescript
// src/types/some-lib.d.ts
declare module 'some-lib';

Better — a typed stub keeps real safety instead of opting the whole module into any:

typescript
declare module 'some-lib' {
  export function doThing(input: string): string;
  export interface Options {
    retries?: number;
    timeout?: number;
  }
  export const version: string;
}
Make sure the compiler sees it

The .d.ts must be inside your tsconfig include scope, or TypeScript won't pick it up. If it's ignored, check include/files in tsconfig.

Last resort — turn off the check#

jsonc
// silences ALL of these, project-wide
{ "compilerOptions": { "noImplicitAny": false } }

Or // @ts-ignore on the import line. Both work and both are bad:

Don't
// @ts-ignore — kills checking for this import and breaks silently when the lib changes
Do
npm i -D @types/some-lib — or a typed declare module stub: error gone, safety kept
When this won't work
  • The package ships ESM-only types but your moduleResolution is node16/nodenext/bundler and its exports map hides the .d.ts — that's a resolution problem, not a missing-types one.
  • Your @types/X version drifted from the runtime package version — reinstall to match, or you'll get wrong types rather than missing ones.

Decision guide#

CheckResultDo this
npm i -D @types/X succeedsCommunity types existDone
Package's package.json has types/typingsIt ships its ownReinstall / fix resolution
Neither existsTruly untypeddeclare module 'X'; (+ a typed stub)
Nothing else and you're in a hurry—@ts-ignore (last resort)

Official references: Consuming declaration files, Type declarations, module .d.ts template, noImplicitAny.

TS7016 in a pnpm or Yarn PnP monorepo#

TypeScript looks for @types packages in the node_modules folder next to the importing file and in every parent folder. What changes between package managers is which packages end up in those folders. If adding all of node_modules to typeRoots produces TS2688 errors across ordinary runtime packages, correct the overly broad typeRoots instead of installing declarations for every reported name.

With npm workspaces, dependencies are hoisted to the root node_modules, so an @types/some-lib declared by any workspace package is visible to all of them. pnpm does not hoist that way: a workspace package's node_modules holds only what its own package.json declares, and the pnpm documentation presents this strictness as deliberate. We tested both on 1 October 2026 (pnpm 12.8.1, TypeScript 5.9.3), with lodash imported from packages/api under strict:

text
@types/lodash declared only in a sibling package (packages/web):
  npm workspaces -> no error (hoisted to the root node_modules)
  pnpm           -> error TS7016: Could not find a declaration file for module 'lodash'.
                    '/p/node_modules/.pnpm/lodash@4.17.21/node_modules/lodash/lodash.js' implicitly has an 'any' type.
 
@types/lodash declared in the root package.json (pnpm add -D -w @types/lodash):
  pnpm           -> no error

So the pnpm symptom is: the types are installed somewhere in the repo, often in the package that was set up first, and the package that imports the library cannot see them. A root-level install is visible to every workspace package, because the root node_modules is a parent folder of all of them. The cleanest fix is to declare the types in the workspace package that imports the library, next to the library itself:

bash
# from the monorepo root, targeting one workspace package
pnpm add -D @types/some-lib --filter api

Yarn Plug'n'Play removes node_modules entirely in favour of a .pnp.cjs resolution map built from each package's declared dependencies, so the same rule applies there: declare the @types package in the workspace that imports the library.

When @types is installed but the version has drifted#

Installing @types/X fixes TS7016 immediately, but DefinitelyTyped versions its type packages to track the major.minor version of the underlying library (its README explains the scheme) — nothing keeps them in sync automatically once both are installed. A package.json that pins some-lib@4 alongside a stale @types/some-lib@2 can resurface a related error (Property does not exist, or a fresh TS7016 if the major-version folder structure changed enough that the old types no longer match) that looks identical to a missing install but isn't fixed by reinstalling the same thing again — the same version-drift shape covered for @types/node itself in the guide to realigning TypeScript and @types/node.

Two commands settle it before guessing:

bash
npm ls @types/some-lib        # what you actually have installed
npm view @types/some-lib versions --json | tail -5   # what DefinitelyTyped currently ships

If the installed major version trails the runtime package's major version, upgrade the types package explicitly (npm install -D @types/some-lib@latest) rather than reinstalling the runtime dependency — the runtime install was never the problem. The inverse also happens: a library that used to need @types/X sometimes starts shipping its own bundled types in a later major release, which makes the separate @types package not just unnecessary but a source of duplicate/conflicting declarations — worth uninstalling once the package's own package.json types field confirms this happened.

Further reading: Fix TS2305: Module Has No Exported Member in TypeScript, Fix "Property does not exist on Window" in TypeScript.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.