From e4a3758030f2baa3a49e38c51b9a82129d2201ac Mon Sep 17 00:00:00 2001 From: Togetic <31132515+Togetic@users.noreply.github.com> Date: Fri, 7 Aug 2026 16:09:50 +0200 Subject: [PATCH] fix(gtm): don't declare `dataLayer` on the global `Window` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GTM registry augments the global `Window` with the whole `GoogleTagManagerApi`, which includes a required `dataLayer: DataLayer & { push: DataLayerPush }`. That makes this package irreconcilable with any other package declaring `Window.dataLayer`. The common case is `@gtm-support/core` (used by `@gtm-support/vue-gtm`), which declares it as optional: declare global { interface Window { dataLayer?: DataLayerObject[] } } The two cannot merge, so the merged `Window` fails its own `extends GoogleTagManagerApi` check and every consumer `Window` augmentation reports TS2430. It is not suppressable from consumer code. `tsc` 5.9.3 does not verify merged interfaces against their bases, so it stays silent. TypeScript 7 (tsgo) does check, and reports it — which this repo will hit itself once #827 lands. Declaring `dataLayer` globally is also inaccurate independently of the conflict: the dataLayer name is configurable via the `l` / `dataLayer` options, so `window.dataLayer` is not guaranteed to exist. The registry never relies on the global declaration for it either — it reads `(window as any)[dataLayerName]` and casts. `window.google_tag_manager` IS read directly, so that member stays declared. `GoogleTagManagerApi` itself is unchanged, so `useScriptGoogleTagManager()` and the `use()` return type keep their existing shape. Consumers should read the dataLayer through that typed proxy, which is also the only access path that respects a custom dataLayer name. --- .../script/src/runtime/registry/google-tag-manager.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/script/src/runtime/registry/google-tag-manager.ts b/packages/script/src/runtime/registry/google-tag-manager.ts index 98a6a967..81a16c84 100644 --- a/packages/script/src/runtime/registry/google-tag-manager.ts +++ b/packages/script/src/runtime/registry/google-tag-manager.ts @@ -72,9 +72,17 @@ export interface GoogleTagManagerApi { /** * Enhanced window type with GTM + * + * `dataLayer` is deliberately NOT declared globally. Its name is configurable through the + * `l` / `dataLayer` options, so `window.dataLayer` is not guaranteed to exist, and declaring it + * here makes this package irreconcilable with any other package that declares `Window.dataLayer` + * (for example `@gtm-support/core`, used by `@gtm-support/vue-gtm`): the two declarations cannot + * merge, so consumers of both get an unsuppressable TS2430 at every one of their own `Window` + * augmentations. Read it through the typed proxy returned by `useScriptGoogleTagManager()` + * instead, which is also the only access path that respects a custom dataLayer name. */ declare global { - interface Window extends GoogleTagManagerApi {} + interface Window extends Pick {} } export { GoogleTagManagerOptions }