TypeScript in 2026: Install & Configure with Node.js 20+

Install the latest TypeScript on Node 20 LTS, align @types/node and @supabase/supabase-js, and fix the errors after a TS upgrade.

Mahdi Ben RhoumaMahdi Ben Rhouma · Updated

The search query "npm typescript latest version" usually ends with a broken build. You run npm install typescript@latest, and the next npm run build throws one of:

bash
src/lib/supabase.ts:3:23 - error TS2307: Cannot find module '@types/node' or its corresponding type declarations.

or, the moment you touch the Supabase client:

ts
const { data, error } = await supabase.auth.getSession()
bash
Property 'auth' does not exist on type 'SupabaseClient<Database, PublicSchemaName>'.

It happens in npm run dev, npm run build, and in CI (Vercel/GitHub Actions) — especially in projects that started on Node.js 16 or 18, or where package.json has a stale engines field. The behavior is identical on macOS, Linux, and Windows.

The invariant that explains every symptom#

TypeScript's type system is only as safe as the type definitions it reads. It erases all types at compile time and adds no runtime checks. So when a TypeScript upgrade breaks your build, the cause is always a compile-time definition mismatch — never a runtime enforcement change.

Three things must move together, or you get the errors above:

  1. The TypeScript version itself.
  2. @types/node — the type definitions for Node.js APIs (Buffer, process, etc.). TypeScript 5.x tightened module resolution, so projects that used to get these transitively now need them installed explicitly.
  3. @supabase/supabase-js — its type definitions are versioned separately from the runtime. An outdated client paired with a newer TS resolver produces Property 'auth' does not exist.

npm's engines field is a warning by default, not a hard block — npm's own package.json documentation confirms it "is advisory only" unless the engine-strict config flag is set — so if you see install failures, look at a package.json engines mismatch or a peer-dependency conflict, not at Node.js itself.

The alignment fix#

bash
# 1. Land on Node.js 20.x LTS
node -v
nvm install 20 && nvm use 20
 
# 2. Move TypeScript, the Node types, and the Supabase client forward together
npm install typescript@latest @types/node@latest @supabase/supabase-js@latest
 
# 3. Declare what you actually run
# in package.json:
#   "engines": { "node": ">=20.0.0" }

Then make tsconfig.json match what Next.js's bundler expects. create-next-app ships these defaults; using NodeNext in a Next.js project breaks resolution because Next.js is not a Node.js ESM loader.

json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "module": "esnext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "jsx": "preserve",
    "incremental": true,
    "paths": { "@/*": ["./src/*"] }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

"module": "esnext" + "moduleResolution": "bundler" is the single change that resolves most "Cannot find module" errors after a TS bump, because it matches what webpack and Turbopack actually do. Per TypeScript's own moduleResolution reference, bundler mode is built specifically for this: unlike the node16/nodenext modes it never requires file extensions on relative imports, mirroring how bundlers resolve modules in practice.

Verify everything lines up#

bash
node -v && npm -v && npx tsc --version
# v20.x.x
# 10.x.x
# Version 5.x.x
 
npx tsc --noEmit
# Found 0 errors.

If Cannot find module '@types/node' persists:

bash
npm install --save-dev @types/node@latest

If Property 'auth' does not exist persists, the Supabase client is still stale:

bash
npm ls @supabase/supabase-js
npm install @supabase/supabase-js@latest

Two stubborn variants#

npm ERR! peer optional dependency failed — @next/eslint-plugin-next has a peer range that does not yet cover the TypeScript you installed. Fix with npm install --save-dev @next/eslint-plugin-next@latest; npm 9+ auto-resolves compatible versions, no manual downgrade needed.

Jest: Cannot find module 'ts-jest' — ts-jest before v29 does not understand TypeScript 5.x. Update jest.config.js to preset: 'ts-jest' and install ts-jest@^29 @types/jest@^29.

Keep it from happening again#

  • Declare "engines": { "node": ">=20.0.0" } in package.json so a mismatched runtime fails fast.
  • Use npm ci (not npm install) in CI to enforce the lockfile.
  • Run npm ls typescript @supabase/supabase-js @types/node before deploying to catch version drift.
  • In Next.js projects, keep "module": "esnext" and "moduleResolution": "bundler" — they are the create-next-app defaults and what the bundler expects.

TS7016 in a pnpm or Yarn PnP monorepo#

Everything above assumes npm's flat node_modules, where a hoisted @types/node anywhere in the tree is usually resolvable from any package. pnpm and Yarn's Plug'n'Play mode both deliberately break that assumption, and a TypeScript upgrade inside a monorepo built on either one produces the same TS7016: Could not find a declaration file for module error for a structurally different reason.

pnpm's node_modules is a symlink farm by design — a package can only resolve dependencies it declares directly in its own package.json, not whatever happens to be hoisted elsewhere in the workspace. If @types/node is installed only at the monorepo root and a workspace package imports something that needs it without declaring it as its own dependency, that package's tsc run fails to find it — even though the exact same file exists on disk two directories up. The fix is to add the type package as an explicit devDependency of the package that actually needs it, not just at the root:

bash
# inside packages/api, not the monorepo root
pnpm add -D @types/node

Yarn PnP goes further: it removes node_modules entirely and resolves packages from a single .pnp.cjs manifest, so any code that expects to find a node_modules folder on disk — including some older tsc plugins and a handful of build tools that walk the filesystem directly instead of using Node's resolution API — breaks regardless of version pinning. When a TypeScript upgrade surfaces resolution errors specifically inside a PnP repo, yarn dlx @yarnpkg/sdks vscode (or the equivalent for your editor/build tool) regenerates the SDK shims that let TypeScript's language service and CLI understand PnP's virtual file layout — a step that has no npm equivalent because npm's flat layout doesn't need it.

Both cases are diagnosable the same way: npm ls-style dependency resolution differs per package manager, so a working tsc --noEmit at the monorepo root proves nothing about whether an individual workspace package can resolve its own types. Run the check from inside the specific package that's failing, not the root, before assuming the fix from the flat-node_modules case above applies unchanged. For the broader class of "cannot find module" declaration errors this overlaps with, see Fix TS7016: Could Not Find Declaration File for Module.

Further reading: TypeScript 6.0 Migration: What Actually Breaks in Next.js/Supabase, Next.js Server Actions with Supabase: Complete Guide.

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

Related fixes & guides