`@next/third-parties` global `Window` augmentation breaks type inference due to index signature interaction with TypeScript mapped types
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://codesandbox.io/p/devbox/xenodochial-scooby-dj58gg?workspaceId=ws_DTjhos2wnm6iYsDknYvGYP
To Reproduce
- Run
nvm install lts/jodto use a recent Node LTS version (required for ESM imports). - Run
npm run typesor open the editor and check the TypeScript diagnostics. - Observe that in
page.tsx,dom.window.documentand related DOM properties are typed asanyinstead ofDocument.
Current vs. Expected behavior
Current:
When @next/third-parties is installed, it globally augments Window with a string index signature ([key: string]: any]).
This causes Omit<Window, ...> and similar mapped types to collapse into { [k: string]: any }.
As a result, downstream libraries and DOM shims (like JSDOM) lose all property type information—document, location, navigator, etc., all become any.
Expected:
The Window type should behave as defined in lib.dom.d.ts: omitting or extending it should preserve concrete properties and not degrade to { [k: string]: any }.
Removing the global index signature from @next/third-parties resolves this and restores proper typing.
Provide environment information
Operating System:
Platform: linux
Arch: x64
Version: #1 SMP PREEMPT_DYNAMIC Sun Aug 6 20:05:33 UTC 2023
Available memory (MB): 4102
Available CPU cores: 2
Binaries:
Node: 22.21.1
npm: 10.9.4
Yarn: 1.22.19
pnpm: 8.10.2
Relevant Packages:
next: 16.0.2-canary.1 // Latest available version is detected (16.0.2-canary.1).
eslint-config-next: N/A
react: 19.2.0
react-dom: 19.2.0
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
TypeScript
Which stage(s) are affected? (Select all that apply)
next build (local)
Additional context
What happened
@next/third-parties augments Window globally as follows:
declare global {
interface Window {
dataLayer?: Object[]
[key: string]: any
}
}
Adding a string index signature ([key: string]: any) makes keyof Window effectively string.
As a result, TypeScript mapped types such as Pick or Omit collapse:
type Example = Omit<Window, "document">
// becomes roughly { [k: string]: any }
All concrete properties of Window are erased, and Example degenerates into a generic indexable object.
This is by design in TypeScript, but causes real issues in downstream packages and apps that rely on Window-based types.
Impact
This global augmentation affects any code that performs type transformations on Window, for example:
- Libraries or tests using DOM shims (e.g., JSDOM)
- Server-side rendering scenarios (SSR/SSG/ISR) that rely on DOM-oriented utilities such as DOMPurify
- Any user code importing or re-declaring
Omit<Window, ...>for isolation, mock, or type refinement
In these cases, properties like document, location, or navigator become any, breaking type safety and IntelliSense.
For instance, @types/jsdom defines a DOMWindow type as:
interface DOMWindow extends Omit<Window, "top" | "self" | "window"> {
...
}
When the global index signature exists, this type collapses to { [k: string]: any }.
Why this occurs
This is not a TypeScript bug.
When a string index signature is present, the compiler treats keyof as string, so mapped types (Pick, Omit, etc.) cannot preserve known keys.
Why this matters for @next/third-parties
In the Google integrations (@next/third-parties/google), the dataLayer key name is dynamic — it defaults to "dataLayer" but can be overridden by the user.
Therefore, declaring a fixed dataLayer property on the global Window type provides limited benefit, while the global string index signature introduces a broad ecosystem regression.
Proposed fix
- Remove the global
Windowaugmentation entirely from@next/third-parties. - Replace global augmentation with a local cast at usage sites:
This retains runtime behavior without polluting global types.const window = globalThis.window as unknown as { [key: string]: Object[] }
This aligns with the goal of PR #68219 while addressing an additional class of breakages observed in the ecosystem.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the global Window augmentation in @next/third-parties and the Google integration code that uses dataLayer. Reproduce the issue with the linked CodeSandbox using npm run types, then verify that removing the broad index signature preserves document, location, and navigator types in Window-based mapped types.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nextjs, typescript
- Domain
- developer-experience, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100