TS7016Strict Mode
Since TS 1.0Updated in TS 2.1

Fix TS7016: Could Not Find a Declaration File for Module

Learn why TypeScript throws TS7016 when an import resolves to untyped JavaScript, and how to fix it with @types, a declare module shim, or allowJs.

error TS7016: Could not find a declaration file for module 'X'. 'Y' implicitly has an 'any' type

What This Error Means

TypeScript error TS7016 means the import worked but the types did not. The compiler found the module on disk — a real JavaScript file in node_modules or in your own source tree — and then failed to find any type information describing it: no bundled index.d.ts, no types/typings field in the package's package.json, no matching @types/... package, and no ambient declare module in your project.

Without declarations, the only type TypeScript could give the imported value is any. The noImplicitAny check — which strict turns on — treats a silent any as an error rather than a fallback. That is why this error is strict-mode only: with noImplicitAny: false the same import quietly becomes any and the error vanishes.

The error names the module specifier first and the JavaScript file it resolved to second, and tsc usually appends a hint:

// The general shape of the error:
// Could not find a declaration file for module 'react-slick'.
//   '/app/node_modules/react-slick/index.js' implicitly has an 'any' type.
//   Try `npm i --save-dev @types/react-slick` if it exists or add a new
//   declaration (.d.ts) file containing `declare module 'react-slick';`

That second path is the fastest way to tell TS7016 apart from its neighbour, TS2307. If the module cannot be resolved at all — wrong path, missing dependency, unhandled .css or .svg import — you get TS2307 ("Cannot find module 'X' or its corresponding type declarations") and no file path. TS7016 always points at a file that really exists.

One historical footnote, because old Stack Overflow answers are confusing: before TypeScript 2.1 this same error number carried a completely unrelated message about a set accessor lacking a type annotation. The number was reused for the declaration-file message in 2.1, so any pre-2.1 discussion of TS7016 is about setters and does not apply here.

Common Causes

1. An Untyped npm Package That Has a @types Companion

The classic case: a JavaScript-only library that never shipped declarations, but the community published them on DefinitelyTyped.

// ❌ Broken
import Slider from 'react-slick'
//                 ~~~~~~~~~~~~~ Error: Could not find a declaration file for module
//                 'react-slick'. '/app/node_modules/react-slick/index.js' implicitly
//                 has an 'any' type.
 
export const carousel = Slider

The fix is a dev dependency, not a code change:

# ✅ Fixed — the declarations live in a separate package
npm i -D @types/react-slick

Once node_modules/@types/react-slick exists, the exact same import compiles clean. Search for the package name on the TypeScript site's DefinitelyTyped search before you write anything by hand — a maintained @types package is always better than a shim you have to keep in sync.

2. No @types Package Exists at All

Plenty of internal SDKs and abandoned libraries have no declarations anywhere. Here you write your own, scoped to the API you actually call.

// ❌ Broken
import { track } from 'legacy-analytics-sdk'
//                    ~~~~~~~~~~~~~~~~~~~~~~ Error: Could not find a declaration file
//                    for module 'legacy-analytics-sdk'.
 
track('checkout_completed', { orderId: 'ord_1024' })

Add a .d.ts file anywhere inside your tsconfig.json includesrc/types/ is a common home — and declare only what you use:

// ✅ Fixed — src/types/legacy-analytics-sdk.d.ts
declare module 'legacy-analytics-sdk' {
  export function track(event: string, props?: Record<string, unknown>): void
}

A bare declare module 'legacy-analytics-sdk' with no body also silences the error, but it types every export as any. That is an acceptable stopgap while you are unblocking a build — it is not a destination. Declaring the two or three functions you call costs a minute and gives you real checking at the call site.

3. Importing Your Own JavaScript File From TypeScript

During a migration you will import a file that has not been converted yet. TypeScript resolves it, refuses to read it, and reports TS7016 against your own source tree.

// ❌ Broken
import { formatMoney } from './vendor/legacy-format.js'
//                          ~~~~~~~~~~~~~~~~~~~~~~~~~~ Error: Could not find a
//                          declaration file for module './vendor/legacy-format.js'.
//                          '/app/vendor/legacy-format.js' implicitly has an 'any' type.
 
export const total = formatMoney(1999)

The one-flag fix is to let the compiler read the JavaScript and infer types from it:

// ✅ Fixed — tsconfig.json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false
  }
}

With allowJs: true the JS file becomes part of the program and formatMoney gets an inferred signature instead of any. Keep checkJs: false if you are not ready to type-check the legacy file itself. If you would rather not pull JavaScript into the program at all, put a hand-written legacy-format.d.ts next to it instead.

4. The Package Ships Types but the Resolver Cannot See Them

This one is maddening because the .d.ts file is visibly sitting in node_modules. Under moduleResolution: node16/nodenext, a package with an exports map is resolved through that map — and if the map has no types condition, the declarations are unreachable no matter where they live.

// ❌ Broken — under --module nodenext, with a package whose exports map
//    lists only "import" and "require"
import { summarize } from 'order-metrics'
//                        ~~~~~~~~~~~~~~~ Error: Could not find a declaration file for
//                        module 'order-metrics'.
//                        '/app/node_modules/order-metrics/dist/index.mjs' implicitly
//                        has an 'any' type.
 
export const count = summarize([])

The real fix belongs upstream — a types condition, listed first in each entry of the exports map:

// ✅ Fixed — the dependency's own package.json
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

Until that ships, unblock yourself with the same declare module shim from cause 2 — it resolves before the exports map is consulted. A related trap: if you set typeRoots yourself, you replace the default, so node_modules/@types stops being searched unless you list it — write "typeRoots": ["./node_modules/@types", "./src/types"], never just ["./src/types"].

How to Fix It

  1. Read the second half of the message. The path after the module name tells you what TypeScript actually resolved to. A path under node_modules means a dependency problem; a path in your own tree means a migration problem (cause 3). No path at all means you are looking at TS2307, not TS7016.

  2. Install @types first. npm i -D @types/<package-name> is the best outcome available: real, maintained, versioned declarations. Check DefinitelyTyped before assuming none exist — the hint tsc prints is a guess, not a confirmation that the package is published.

  3. Write a scoped declare module shim second. Put a .d.ts inside your include and declare the exports you call, with real signatures. Confirm the file is part of the program — a declaration outside include, or hidden by a custom typeRoots, is invisible and the error will not budge.

  4. Turn on allowJs for your own JavaScript. For a file in your repo that is on its way to TypeScript, allowJs: true beats writing declarations for code you are about to delete. Add checkJs: true later, per file, as you tighten things up.

  5. Do not reach for the escape hatches. // @ts-ignore on the import hides the error and leaves the import any, so every call through it is unchecked. Setting noImplicitAny: false is worse — it disables implicit-any checking for the whole project to fix one import, and it will take TS7006 and TS7053 down with it. If you truly need a temporary bypass, a bare declare module 'name' is the narrowest one: it is scoped to a single package and it is grep-able when you come back to finish the job.

FAQ

What causes TypeScript error TS7016?

TS7016 fires when TypeScript resolves an import to a real JavaScript file but finds no type declarations for it — no bundled index.d.ts, no types field in the package's package.json, no @types package and no ambient declare module. The import would have to be typed any, and noImplicitAny (part of strict) rejects that. The distinguishing detail is that the module was found: the message quotes the exact .js file it landed on. If nothing was found at all, the error is TS2307 instead.

How do I write a declare module file for an untyped npm package?

Create a .d.ts file anywhere inside your tsconfig.json includesrc/types/pdf-merger.d.ts, for example — and declare the module by its exact import specifier:

declare module 'pdf-merger' {
  export function merge(files: string[]): Promise<Uint8Array>
  export function pageCount(file: string): Promise<number>
}

Only declare what you actually import; you can grow the file as you use more of the API. A bare declare module 'pdf-merger' with no body compiles too, but it types the entire package as any, so nothing you call through it is checked. If the package is deep-imported ('pdf-merger/lib/streams'), you need a separate declare module block for each specifier you use.

Is it safe to fix TS7016 by setting noImplicitAny to false?

No, and it is a much bigger change than it looks. noImplicitAny: false does make TS7016 disappear, but the flag is project-wide: you also lose TS7006 on untyped parameters, TS7053 on unchecked index access, and every other implicit-any diagnostic in every file. You have traded a whole class of type safety for one untyped import. Keep the flag on and fix the import — @types package, declare module shim, or allowJs for your own JavaScript, in that order.

Related Errors

Practice This

Browse all TypeScript practice challenges to keep sharpening your type-level skills.

Share this reference

Become a TypeScript Pro

Track your progress through 100+ hands-on challenges. Free, sign in with GitHub.

Or start solving right away: explore all TypeScript challenges