Could not find a declaration file for module 'module-name'.
Developer Guide

Could not find a declaration file for module 'module-name'.

Complete guide to could not find a declaration file for module 'module-name'.. Covers root causes, diagnostic steps, and working code fixes.

2026-06-18
7 min read
Could not find a declaration file for module 'module-name'.

title: 'Fix "Could not find a declaration file for module" in TypeScript' description: 'Resolve the "implicitly has an any type" error by installing @types or creating .d.ts declaration files — step-by-step fix with verification.' excerpt: 'You see "Could not find a declaration file for module X" and "/path/to/file.js implicitly has an any type". Here’s how to fix it reliably — no guesswork.' image: 'https://images.unsplash.com/photo-1547082299-de196ea013d6?auto=format&fit=crop&w=1200&q=80' category: 'TypeScript' tags:

  • TypeScript
  • Node.js
  • troubleshooting date: '2026-06-18' modifiedDate: '2026-06-18' readTime: '8 min read' author: 'Mahdi Br' keywords:
  • Could not find a declaration file for module
  • implicitly has an any type error
  • fix TypeScript module declaration
  • TypeScript module resolution troubleshooting faqSchema: '@context': 'https://schema.org' '@type': FAQPage mainEntity:
    • '@type': Question name: 'Why does TypeScript say "Could not find a declaration file for module"?' acceptedAnswer: '@type': Answer text: 'TypeScript throws this error when it cannot locate type declarations (.d.ts files) for an imported JavaScript module. It happens because the module lacks a types field in package.json, or you haven’t installed the corresponding @types package from DefinitelyTyped.'
    • '@type': Question name: 'How do I fix "implicitly has an any type" for a local module?' acceptedAnswer: '@type': Answer text: 'Create a custom declaration file (e.g., src/types/module-name.d.ts) that declares the module with its exports, then ensure tsconfig.json includes the src/types directory in typeRoots or types. For npm packages, install @types/package-name if available.'

TL;DR#

If you’re seeing Could not find a declaration file for module 'module-name'. '/path/to/module-name.js' implicitly has an 'any' type, the cause is usually that TypeScript cannot locate type declarations for the imported JavaScript module — either because the package doesn’t ship with types, or you haven’t installed the @types package, or you’re importing a local .js file without a .d.ts declaration.

Fix it by installing @types/module-name (if it exists on DefinitelyTyped) or creating a custom declaration file like src/types/module-name.d.ts that declares the module’s exports.

If that doesn’t work, scroll to verify the fix — there are two common variants this guide also covers.

What you'll see#

plaintext
Could not find a declaration file for module 'lodash-es'. '/Users/mahdi/project/node_modules/lodash-es/lodash.js' implicitly has an 'any' type.
  Try `npm install @types/lodash-es` if it exists or add a new declaration (.d.ts) file containing `declare module 'lodash-es';`

It happens when you import a JavaScript module — local or third-party — without type declarations, and TypeScript’s compiler (tsc) runs in strict mode (or noImplicitAny: true). The behavior is the same across Node.js, Next.js, and plain TypeScript projects.

This error appears in three common contexts:

  1. Third-party JS package (e.g., import _ from 'lodash-es'lodash-es has no types field in package.json, and @types/lodash-es is missing).
  2. Local .js file (e.g., import { formatDate } from '../utils/formatDate' where formatDate.js has no sibling formatDate.d.ts).
  3. Custom module (e.g., import config from './config.js' in a monorepo where config.js is not typed).

TypeScript must infer a type for every expression. When it can’t find declarations, it falls back to any, but in strict mode it refuses to do so silently — instead, it errors with this message. The compiler is enforcing type safety, not failing.

Root cause#

TypeScript resolves type definitions for modules in two ways, drawing on TypeScript declaration files: ambient declarations (for global or external modules) and module declarations (for import/export-based modules). When you import something, TypeScript looks for .d.ts files in this order:

  1. The package’s types or typings field in node_modules/package-name/package.json.
  2. A corresponding .d.ts file in the same directory (e.g., module-name.d.ts next to module-name.js).
  3. Any .d.ts files in directories listed in typeRoots (default: node_modules/@types).
  4. Custom declaration files referenced in tsconfig.json via typeRoots or types.

If none of these exist, TypeScript cannot infer the module’s shape — so it treats it as any, which violates noImplicitAny. Hence the error.

The relevant code path is:

ts
// src/services/apiClient.ts
import { formatDate } from '../utils/formatDate'; // ← triggers error here
 
export const fetchUser = async (id: string) => {
  const date = formatDate(new Date()); // ← `formatDate` is implicitly `any`
  return { id, date };
};

And formatDate.js:

js
// src/utils/formatDate.js
export const formatDate = (date) => {
  return date.toISOString().split('T')[0];
};

TypeScript sees formatDate.js, but no formatDate.d.ts. It doesn’t know formatDate returns a string, so it can’t validate usage — and in strict mode, it refuses to let any slide.

The deeper issue is that TypeScript’s module resolution is opt-in for types. JavaScript packages rarely ship .d.ts files (they’d rather keep package.json minimal), and local JS files never do. So unless you explicitly provide declarations, TypeScript has no way to know the module’s API.

The fix#

The fix depends on whether the module is third-party or local.

For third-party packages, install npm @types packages (@types/<package>) if available:

bash
npm install --save-dev @types/lodash-es

For local modules, create a .d.ts file in src/types/ (or alongside the .js file) and declare the module:

ts
// src/types/formatDate.d.ts
declare module '../utils/formatDate' {
  export function formatDate(date: Date): string;
}

That single change addresses the cause because TypeScript now has a .d.ts file to consult during resolution — it no longer needs to guess, and it can validate that formatDate takes a Date and returns a string.

Step by step#

  1. Identify the failing import — check the error message for the exact module path (e.g., ../utils/formatDate).
  2. Create a declaration file — in src/types/formatDate.d.ts, add the module declaration.
  3. Ensure TypeScript sees it — update tsconfig.json to include src/types in typeRoots or types.
  4. Restart the dev servertsc --watch or next dev will re-resolve types.

Here’s the full tsconfig.json snippet showing the relevant compiler options to make sure declarations are picked up:

json
{
  "compilerOptions": {
    "typeRoots": ["./src/types", "./node_modules/@types"],
    "types": ["node"]
  },
  "include": ["src/**/*"]
}

The typeRoots array tells TypeScript to look in ./src/types first, before node_modules/@types. This is critical — otherwise, your custom declarations won’t be found.

Verify the fix#

Run:

bash
npx tsc --noEmit

You should see:

text
No errors.

Instead of:

text
src/services/apiClient.ts:2:28 - error TS7016: Could not find a declaration file for module '../utils/formatDate'. '/Users/mahdi/project/src/utils/formatDate.js' implicitly has an 'any' type.

If you’re using Next.js, restart the dev server:

bash
npm run dev

Then visit a page that imports the module — the error should be gone from the terminal and browser console.

If you’re still seeing the error, two common variants exist:

Variant A — Module path uses .js extension in import#

TypeScript expects imports without .js extensions in tsconfig.json when moduleResolution: "bundler" or "node16" (Next.js 13+ default), but some tools (like Vite) require .js in imports.

If your error says Could not find a declaration file for module './config.js', but your import is import config from './config.js', TypeScript looks for config.js.d.ts, not config.d.ts.

Fix it by either:

  • Removing .js from the import (import config from './config') and ensuring config.d.ts exists, or
  • Creating config.js.d.ts (yes, with the .js in the filename) and declaring:
ts
// src/types/config.js.d.ts
declare module './config.js' {
  const config: { api: string; timeout: number };
  export default config;
}

Variant B — Local module is in node_modules (e.g., symlinked or linked package)#

If you’re developing a local package and linking it with npm link, TypeScript may not resolve its .d.ts files because node_modules resolution skips types fields for linked packages.

Fix it by adding the package’s source directory to typeRoots:

json
{
  "compilerOptions": {
    "typeRoots": ["./src/types", "./node_modules/@types", "../my-local-package/src/types"]
  }
}

Or, better yet, ensure the linked package has "types": "./dist/index.d.ts" in its package.json and build it first.

Why this happens (and how to avoid it next time)#

TypeScript’s strictness is a feature, not a bug — it prevents runtime errors by catching missing types at compile time. But the tradeoff is friction when working with JavaScript modules.

The underlying invariant is: TypeScript only knows what .d.ts files tell it. If a module has no .d.ts, it’s any — and in strict mode, that’s a compile error.

To prevent regressions, adopt these three practices:

  1. Install @types packages proactively — before importing, check npm search @types or visit DefinitelyTyped. If a package has types but isn’t installed, you’ll get this error.

  2. Use tsc --noEmit in CI — add it to your linting pipeline. This catches missing declarations before they ship to production.

  3. Prefer TypeScript-first modules — when building internal packages, ship .d.ts files and set "types": "./dist/index.d.ts" in package.json. For local JS files, create a src/types directory and declare modules there — it’s faster than hunting down errors later.

I cover this in detail in JS to TypeScript: Incremental Migration, No Full Rewrite, where we walk through adding types to legacy JS codebases without rewriting them.

FAQ#

Q: Do I need to install @types/node?
A: Yes — if you’re using Node.js built-ins (fs, path, process), install @types/node as a dev dependency. Without it, imports like import fs from 'fs' will fail with the same error.

Q: Can I disable the error instead of fixing it?
A: You can — by setting "noImplicitAny": false in tsconfig.json — but you shouldn’t. That disables type safety for all modules, not just this one. The correct fix is always to provide declarations.

Q: Why does declare module need the exact path?
A: TypeScript matches declarations by string literal. If your import is import { x } from '../utils/formatDate', the declaration must use declare module '../utils/formatDate' — no trailing .js, no relative path aliases unless you configure them in paths.

Q: What if the module exports a default function?
A: Use export default in the declaration:

ts
declare module '../utils/formatDate' {
  export default function formatDate(date: Date): string;
}

Then import formatDate from '../utils/formatDate' works with full types.

Each of these guides extends the same core idea: working with TypeScript's strictness rather than around it, so your code stays correct as your project grows.

One email a month — no fluff

RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.