Fix Cannot find name 'console' in TypeScript (TS2584)

Fix Cannot find name 'console' in TypeScript (TS2584)

# typescript# tsconfig# node# troubleshooting
Fix Cannot find name 'console' in TypeScript (TS2584)Mahdi BEN RHOUMA

TS2584: your lib has no dom and Node's types aren't loaded. Install @types/node and list it in tsconfig types; TypeScript 6.0 stopped auto-loading.

TL;DR

Cannot find name 'console' means no file in your compilation declares console. TypeScript only gets it from three places: the dom lib, the webworker lib, or the @types/node package. The error appears when your tsconfig.json sets lib to ECMAScript-only entries and Node's types are not loaded. For Node code, run npm install --save-dev @types/node and add "types": ["node"] to compilerOptions. On TypeScript 6.0 and later that second step is no longer optional, because types now defaults to an empty list.

The error

The Stack Overflow question behind this page shows the classic case: a plain console.log(message) inside a function, flagged by the editor as [ts] Cannot find name 'console'. A current compiler adds a code and a hint. Run npx tsc --noEmit on the same kind of project and you get:

src/index.ts(2,3): error TS2584: Cannot find name 'console'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.
src/index.ts(3,15): error TS2591: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
Enter fullscreen mode Exit fullscreen mode

Pay attention to the second line when it appears. TS2591 for process confirms the real problem: Node's type definitions are missing from the program. The console hint suggests dom only because dom is one of the libs that declares it.

Why it happens

Two separate tsconfig.json settings decide whether console exists.

lib controls the built-in declarations. If you leave lib out, TypeScript loads a default set that, per the lib reference, covers built-in JavaScript APIs and "type definitions for things found in browser environments (like document)". That default set includes console. Once you write "lib": ["es2022"], you replace the default with exactly that list. The ES libs describe the language, and the language has no console. The only built-in declarations of it are in lib.dom.d.ts and lib.webworker.d.ts.

types controls which global @types packages load. Node's console is declared by console.d.ts in @types/node, and it only counts if that package is in the program. The types reference spells out the rules:

  • Before TypeScript 6.0, with no types field, "all visible @types packages are included", meaning every package under node_modules/@types in any parent folder.
  • With an explicit types list, "only packages listed will be included in the global scope". A list such as ["jest"] or ["reflect-metadata"] silently drops @types/node, and with it console and process. This is the case the accepted answer on the question warns about.
  • From TypeScript 6.0, the default is an empty list. The TypeScript 6.0 release notes say it directly: "In TypeScript 6.0, the default types value will be []", and "You will likely need to add "types": ["node"]". The stated reason is build speed, since enumerating every @types package "can be very expensive".

Put those together and you get the full picture of when tsc reports the error:

lib types @types/node installed TS 5.9 TS 6.0+
not set not set no console fine, process fails same
["es2022"] not set yes passes TS2584 on console
["es2022"] ["node"] yes passes passes
["es2022"] list without node yes TS2584 on console TS2584 on console
["es2022"] ["node"] no TS2688 TS2688

The second row explains why many projects hit this error for the first time after upgrading the compiler: nothing changed in tsconfig.json, but @types/node stopped being loaded implicitly. The upgrade has other breaking defaults too, covered in the TypeScript 6.0 migration guide for Next.js and Supabase. That guide does not cover the types change, so check both.

Fix

The fix depends on where the code runs. For a Node.js service, script or CLI, follow steps 1 and 2. Add step 3 if you use a test runner with global functions, and step 4 only for code that genuinely runs in a browser.

1. Install the Node type definitions

Install @types/node as a dev dependency, pinned to the major version of the Node runtime you deploy on (Node 22 here):

npm install --save-dev @types/node@22
Enter fullscreen mode Exit fullscreen mode

The DefinitelyTyped README explains that an @types package's major and minor numbers track those of the library it describes. Matching the major stops the compiler from accepting APIs your runtime does not have. If the compiler and the type packages have drifted apart after a blanket upgrade, aligning TypeScript and @types/node covers realigning both in one pass. That page installs @types/node@latest; for a deployed service, keep @types/node on your runtime's major instead.

2. List node in compilerOptions.types

Add node to types explicitly. This is the only configuration that behaves the same on TypeScript 5.9 and 6.0+. A complete tsconfig.json for a Node project:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "lib": ["es2022"],
    "types": ["node"],
    "strict": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"]
}
Enter fullscreen mode Exit fullscreen mode

If your config already had a types list, add "node" to it rather than deleting the list. Deleting it on 6.0 leaves you with the empty default and the same error.

3. Add your test runner's types next to node

Test files use globals such as describe and it, which are a second global package. Install the runner's types and list both. The release notes use this exact pair as their example:

npm install --save-dev @types/jest
Enter fullscreen mode Exit fullscreen mode
{
  "compilerOptions": {
    "target": "es2022",
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "lib": ["es2022"],
    "types": ["node", "jest"],
    "strict": true
  },
  "include": ["src", "test"]
}
Enter fullscreen mode Exit fullscreen mode

4. Browser code only: add dom to lib

The second-most-voted answer on the question is to add "dom" to lib. That fix is correct for code that runs in a browser (a React or Next.js front end, for example), where window, document and console all exist at runtime:

{
  "compilerOptions": {
    "target": "es2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "lib": ["es2022", "dom"],
    "jsx": "react-jsx",
    "strict": true
  },
  "include": ["src"]
}
Enter fullscreen mode Exit fullscreen mode

For Node-only code it is the wrong fix, despite what the TS2584 hint says. dom declares window, document and localStorage as globals. A server file that touches document.title would then type-check and crash at runtime with a ReferenceError, which is exactly the kind of mistake the compiler is there to stop. Browser globals that you add yourself belong in a global declaration instead, as shown in how to add a property to window in TypeScript.

Verify the fix

Type-check without emitting output:

npx tsc --noEmit
Enter fullscreen mode Exit fullscreen mode

It should exit with no errors. To confirm that @types/node is loaded, and why, use --explainFiles, which prints every file in the compilation with the reason it was included:

npx tsc --noEmit --explainFiles | grep -A1 "^node_modules/@types/node/index.d.ts$"
Enter fullscreen mode Exit fullscreen mode

With "types": ["node"] in place, the line under the file reads Entry point of type library 'node' specified in compilerOptions. If grep prints nothing, the package is not in the program and console will fail again.

When several configs extend each other, print the final merged configuration to see which lib and types actually apply:

npx tsc --showConfig
Enter fullscreen mode Exit fullscreen mode

A types or lib value inherited through extends from a shared base config is a common reason a fix appears to do nothing.

Edge cases the answers skip

"types": ["*"] is TypeScript 6.0+ only. The release notes offer * to restore the old "load everything" behaviour. On 5.9 the compiler does not recognise it and fails with error TS2688: Cannot find type definition file for '*'. Even on 6.0 it brings back the build cost the change was made to remove (the release notes report 20-50% faster builds from setting types appropriately), so list packages by name instead.

"node" listed but not installed. If types names a package that is not installed, the error changes to error TS2688: Cannot find type definition file for 'node'. In a pnpm or Yarn workspace this usually means @types/node is installed in another package but not resolvable from the package being compiled. Install it in the package that owns the tsconfig.json.

The triple-slash workaround. Placing /// <reference types="node" /> at the top of a file also brings @types/node in. The triple-slash directives handbook is explicit that .ts files should use types in tsconfig.json instead and reserves the directive for hand-written declaration files. Because the directive pulls the package into the whole compilation, one stray reference also hides the real configuration problem from every other file.

types does not affect imports. The types option only governs global declarations. An imported package is still typed through its own declarations or its @types package, whatever the types list says. If the error is about a module rather than a global name, you have a different problem: see TS7016, could not find a declaration file for module.

Mixed browser and Node code in one repository. A single config cannot be honest about both runtimes, so split them. The editor's language service only discovers files named tsconfig.json (or jsconfig.json), so a standalone tsconfig.node.json is ignored in the editor unless something points to it. The simplest layout gives each runtime its own folder with its own tsconfig.json: one with dom for the front end, one in the server folder with an ES-only lib and "types": ["node"]. If both configs must live at the root, make the root tsconfig.json a solution file that references them:

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Each referenced config needs "composite": true and its own include. On the command line, build them with npx tsc -b, or check one with npx tsc -p tsconfig.node.json --noEmit. Plain npx tsc on a root file with "files": [] compiles nothing. With either layout the editor reports console, process and document correctly for each part of the codebase.

The error message points at lib, but in a Node project the missing piece is almost always the types list. Load Node's types explicitly, keep dom for code that runs in a browser, and the same configuration will keep compiling across TypeScript upgrades.


Originally published at https://www.iloveblogs.blog