TS2694Semantic Error
Since TS 2.1

Fix TS2694: Namespace Has No Exported Member

Learn why TypeScript throws TS2694 when a namespace is missing the member you asked for, and how to fix React.JSX, NodeJS.Global and stale @types.

error TS2694: Namespace 'X' has no exported member 'Y'

What This Error Means

TS2694 means you used a qualified type reference like React.JSX.Element, NodeJS.Global or Shapes.Square, TypeScript resolved the part before the dot to a real namespace, and then failed to find the name after the dot inside it. The namespace exists. The member does not.

That is a more specific signal than it looks. It tells you the declaration files that own the namespace did reach the compiler — so nothing is missing from your install in the crude sense — but the shape of those declarations is not the shape your code expects. Since namespaces like React, NodeJS and Express come from @types packages rather than from the language, the usual cause is a version skew: the member was added in a later release than the one you have, removed in a release newer than the code that references it, or it lives in a companion package you never installed.

declare const orderCache: NodeJS.Global
                          ~~~~~~~~~~~~~
error TS2694: Namespace 'NodeJS' has no exported member 'Global'.

The neighbouring errors are worth telling apart, because they point at different fixes. TS2503 (Cannot find namespace 'X') means the namespace was never found — a missing package, not a wrong version. TS2305 is the module equivalent: same idea, but for a named import from a module rather than a member of a namespace. TS2339 is the value-side equivalent, fired on property access at runtime positions rather than in a type position. And TS2702 means the left side resolved to something that is not a namespace at all.

Common Causes

1. React.JSX On A Version Of @types/react That Predates It

React's type definitions only gained a nested JSX namespace in @types/react 18.2. Code written against newer types — yours, or a dependency's .d.ts — fails against an older copy of the declarations.

// ❌ Broken — @types/react 18.0.x, jsx: "react-jsx"
export function OrderBadge({ label }: { label: string }): React.JSX.Element {
  //                                                      ~~~~~~~~~~~~~~~~~
  // Error: Namespace 'React' has no exported member 'JSX'. (TS2694)
  return <span className="badge">{label}</span>
}
# ✅ Fixed — the member exists in current declarations, so upgrade them
npm install --save-dev @types/react@latest @types/react-dom@latest
// ✅ Also fine — an annotation that is stable across every @types/react version
export function OrderBadge({ label }: { label: string }): React.ReactElement {
  return <span className="badge">{label}</span>
}

This is the single most reported form of TS2694, and it usually appears inside node_modules rather than in your own source — antd 5, React Router 6.16+ and rc-virtual-list all reference React.JSX in their published declarations. If the error names a file you did not write, your project has an old @types/react and a dependency that assumes a new one. Upgrading is the real fix; "skipLibCheck": true silences declaration files while you wait for a release, but it silences every other library's type errors too, so treat it as a temporary unblock and not a setting you leave on by accident.

Note the mirror image: React 19 removed the global JSX namespace, so JSX.Element on new types fails with TS2503 while React.JSX.Element on old types fails with TS2694. Seeing both at once means two copies of @types/react are in play.

2. NodeJS.Global After @types/node 16

@types/node removed the Global interface from the NodeJS namespace in version 16. The pattern lives on in copy-pasted Jest setup files and in the "one Prisma client in dev" singleton, which is why the error tends to show up during an unrelated dependency bump.

// ❌ Broken — @types/node 16 or newer
declare const orderCache: NodeJS.Global
//                        ~~~~~~~~~~~~~
// Error: Namespace 'NodeJS' has no exported member 'Global'. (TS2694)
 
export function readCache(): unknown {
  return orderCache
}
// ✅ Fixed — declare the global directly and read it through globalThis
declare global {
  var orderCache: Map<string, number> | undefined
}
 
export const orderCache = globalThis.orderCache ?? new Map<string, number>()
globalThis.orderCache = orderCache

declare global only works inside a module, so the file needs at least one top-level import or export — that is the most common follow-up stumble. The var keyword is also required rather than stylistic: let and const declarations in a global block do not become properties of globalThis, so the compiler rejects the read on the next line.

The upside of the replacement is that it is plain ECMAScript. globalThis exists in browsers, Node and edge runtimes alike, so the same augmentation compiles in a Next.js app that renders on both sides.

3. The Member Lives In A Companion Package You Did Not Install

Some namespaces are assembled from several declaration packages by declaration merging. Express is the classic one: @types/express contributes Express.Request, and @types/multer is what adds Express.Multer. Install one and not the other and the namespace resolves while the member does not.

// ❌ Broken — @types/express is installed, @types/multer is not
import type { Request } from "express"
 
export function uploadedName(req: Request, file: Express.Multer.File): string {
  //                                             ~~~~~~~~~~~~~~~~~~
  // Error: Namespace 'global.Express' has no exported member 'Multer'. (TS2694)
  return `${req.ip}-${file.originalname}`
}
# ✅ Fixed — install the package that declares the member
npm install --save-dev @types/multer

The prefix in the message is a hint worth reading. 'global.Express' tells you TypeScript resolved the global Express namespace rather than a local one, which narrows the search to declaration files that augment the global scope. When the message names a bare 'Express' instead, the namespace it found is one declared in your own project — and the fix is in your code, not in package.json.

4. A Member Of Your Own Namespace That Is Not Exported

The one case that is genuinely about your code. Members of a namespace are private unless you mark them export, exactly like module exports — an interface declared without the keyword is visible inside the namespace body and nowhere else.

// ❌ Broken — Square is declared, but not exported
namespace Shapes {
  export interface Circle { radius: number }
  interface Square { side: number }
}
 
const tile: Shapes.Square = { side: 4 }
//          ~~~~~~~~~~~~~
// Error: Namespace 'Shapes' has no exported member 'Square'. (TS2694)
// ✅ Fixed — export the member so the qualified name can reach it
namespace Shapes {
  export interface Circle { radius: number }
  export interface Square { side: number }
}
 
const tile: Shapes.Square = { side: 4 }

A misspelling produces the same error with no "did you mean" suggestion, because TS2694 has no spelling-correction variant the way TS2551 does for properties. If the member looks exported and spelled right, check that you are reading the file the compiler is: merged namespaces span declarations, and the one you are editing may not be the one in the program.

How to Fix It

  1. Work out which package owns the namespace. React comes from @types/react, NodeJS from @types/node, Express from @types/express and its add-ons, JSX from whichever JSX runtime is configured. Run npm ls @types/react (or the relevant package) and read the tree — the owner, and its version, decide every fix below. For a namespace declared in your own source, jump to step 5.

  2. Upgrade the declarations when the member was added later. npm install --save-dev @types/react@latest fixes the React.JSX family outright, because the member genuinely does not exist in the version you have. Keep @types/react and @types/react-dom on matching majors; upgrading only one of them reintroduces the skew in a less obvious place.

  3. Dedupe when the version looks right but the error persists. npm ls printing two copies of the same @types package means one dependency resolved its own nested version, and the compiler may be reading the older one. An overrides entry in package.json (resolutions for Yarn) pins a single copy; delete node_modules and the lockfile entry, then reinstall and check the tree again.

  4. Replace members that were removed rather than chasing them. NodeJS.Global is gone for good — use declare global plus globalThis. Prefer annotations that do not depend on namespace layout at all: React.ReactElement over React.JSX.Element, ReturnType<typeof setTimeout> over NodeJS.Timeout. These survive the next major bump of the declarations.

  5. Export the member if the namespace is yours. Add export in front of the interface, type alias or const, and confirm the declaration is in a file the program actually includes. If the namespace is spread over several files, the member must be exported in the declaration the compiler loads, not just in the one open in your editor.

  6. Do not reach for any or a hand-written namespace. Annotating the value as any hides the mismatch and the type checking with it, and writing your own declare namespace React { … } shadows the real declarations with a copy that will drift out of date within a release. "skipLibCheck": true is defensible as a short-term unblock for errors inside node_modules, but it is not a fix for an error in your own code. Pin the @types versions in devDependencies so a fresh npm ci reproduces a program that compiles.

FAQ

What causes TypeScript error TS2694?

TS2694 fires on a qualified type reference where the namespace resolved but the member after the dot did not. In NodeJS.Global, TypeScript finds the NodeJS namespace, looks for an exported Global inside it, and reports this error when there is none. Because these namespaces are almost always contributed by @types packages rather than by your own code, the cause is normally a version mismatch — the member was added in a newer release of the declarations, removed in a newer one, or it lives in a companion package that is not installed. The contrast with TS2503 is the quickest diagnostic you have: TS2503 means the declarations never reached the compiler at all, while TS2694 means they did and disagreed with your code.

How do I fix Namespace 'React' has no exported member 'JSX'?

Upgrade the React type definitions, because the nested JSX namespace only exists from @types/react 18.2 onwards:

[object Object]

If the error names a file inside node_modules, the code is a dependency's — antd, React Router and similar libraries reference React.JSX in their published declarations — and your project simply resolved an older copy of the types. Run npm ls @types/react; two entries mean you need an overrides pin rather than another install. "skipLibCheck": true will get CI moving while you sort that out, but it suppresses every declaration-file error in the project, so remove it once the versions line up. In your own code, React.ReactElement is the annotation that never breaks on a types upgrade.

How do I replace NodeJS.Global after upgrading @types/node?

NodeJS.Global was removed in @types/node 16, so the augmentation snippet that has been circulating in Jest and Prisma setups since 2019 no longer compiles. Declare the global variable directly instead:

declare global {
  var prismaClient: PrismaClient | undefined
}
 
export const prisma = globalThis.prismaClient ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalThis.prismaClient = prisma

Two details decide whether this compiles. The file has to be a module — a top-level import or export — or declare global is an error in its own right. And the declaration has to use var; let and const do not create properties on globalThis, so the read on the following line will not typecheck. The write-back is what makes it a singleton rather than a factory: without it globalThis.prismaClient stays undefined and every module evaluation — every hot reload in dev — opens another client, which is the connection exhaustion the pattern exists to prevent. The same pattern replaces the other removed names in that namespace, and it keeps working in runtimes where Node's globals are not available at all.

Related Errors

Practice This

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

Related Concepts

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