Using globalThis in Typescript
Asked Answered
M

9

43

I am trying to use globalThis in TypeScript and I would like some suggestions on how to write it in a better way.
Current implementation is like this:

Create a file types/global.d.ts and add inside

interface Global {
   foo: string
}

declare let foo: Global["foo"];

in tsconfig.json add

"files": [
  "types/global.d.ts"
]

Then in order to set the value of foo use

(globalThis as any).foo = "The value of foo"

What I don't like with this approach is first the boilerplate needed (but I think this cannot be avoided) and second the (globalThis as any).foo = expression

Mistrust answered 23/12, 2019 at 17:43 Comment(3)
My first question would be the use of globalThis and if there is a solution available that doesn't require it. Can foo be referenced in a local scope or reduced scope rather than using a global object?Fleabitten
@AustinMehmet for this specific case the use of globalThis is needed. window object can still work for me. But the need for a global foo is requiredMistrust
No way to use any higher order wrappers to avoid globalThis either?Greaser
I
76

Update 2021 October

Applies to TypeScript 4.3+

Error

Element implicitly has an any type because type typeof globalThis has no index signature. ts(7017)

Solution

declare global {
  function myFunction(): boolean;
  var myVariable: number;
}

globalThis.myFunction = () => true;
globalThis.myVariable = 42;
  • IMPORTANT: This solution only works by declaring variables with var (do not use let or const).

Background

See the discussion on TypeScript issue 30139.

Traditionally, the way to specify a TypeScript declare-block in a Node.js context was as follows:

// Does not work as of October 2021 (TypeScript 4.3+)
declare global {
  module NodeJS {
    interface Global {
      myFunction(): boolean;
      myVariable: number;
    }
  }
}

tsconfig.json: noImplicitAny

Note that this error will be suppressed if the TypeScript setting noImplicitAny is set to false. It is recommended to enable noImplicitAny for better type checking.

Intellectualize answered 3/10, 2021 at 21:40 Comment(3)
Wow! That var instead of let/const was a life-saver!Vizzone
interesting, thanks!! but why only with var?Wondawonder
@GianlucaCasati see https://mcmap.net/q/47766/-why-don-39-t-const-and-let-statements-get-defined-on-the-window-object-duplicateIntellectualize
F
20

My answer is based on the one by Edward Casanova. I was about to edit it, but there are still quite a few differences to the code that works for me, plus I can now give some additional information on this.

This is based on TypeScript 4.3.5

// typings/globals.d.ts (depending on your tsconfig.json)

export {}

interface Person {
  name: string
}

declare global {
  var someString: string
  var globalPerson: Person
}

This does not work without at least one export (or import) keyword. This turns this file into an ES module, which is necessary for this to work. You can export any of the statements or add an empty export {}.

The code above adds types for the following handles:

someString
window.someString
globalThis.someString

globalPerson.name
window.globalPerson.name
globalThis.globalPerson.name

On the other side, the following is possible, but the result is different:

export {}

declare global {
  let someLetString: string
  const someConstString: string
}

This only adds types for the following handles:

someLetString
someConstString

As expected, you cannot assign any value to someConstString. This might be useful for some older JS libraries that add the functionality to the global scope instead of exporting it. Libraries still can assign values to const in this case because it's just a type, not a real const. This const is only known to TypeScript code. But be aware that let and const don't add properties to the global objects window and globalThis. So, var might be the better choice here after all.

Fouquet answered 20/7, 2021 at 9:39 Comment(2)
First answer that worked for me, thanks. Obviously you need to put export {} at the top. Duh. /sGromme
This is super useful information that should be included in the official documentation. These differences are so nuanced, it gives people a sense of, "I swear this worked for me before!"Pizzeria
M
11

Using a combination of the other answers, here is what ended up working for me.

At the top of the TS file (or in a global typings file):

declare module globalThis {
    let myObj: { foo: string };
}
globalThis.myObj = { foo: 'bar' };
Mastrianni answered 7/11, 2020 at 2:1 Comment(2)
Thank you, this worked for me in TS 4.0.5 after all other answers didn't!Topdrawer
Declaration name conflicts with built-in global identifier 'globalThis'Farinose
F
5

You can use declaration merging in typescript to achieve this.

In your global.d.ts file:

export declare global {
  interface Window {
    // add you custom properties and methods
    foo: string
  }
}

Now you can use Window.foo without typescript warning you.

I wrote a mini blog about this on dev.to

Fetich answered 23/12, 2019 at 18:15 Comment(8)
Thank you for your answer. I get an error on declare global saying TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.Mistrust
Edited to add export keyword at the start of lineFetich
I also have the global.d.ts file at the root of the project (same directory as tsconfig.json)Fetich
I don't know. Maybe I am missing something. Now I get TS2668: 'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.Mistrust
what version of typescript you using? and what vertion of the typescript compiler: tsc --v or npx tsc --vFetich
Version 3.7.2 on bothMistrust
How about this: > I also have the global.d.ts file at the root of the project (same directory as tsconfig.json)Fetich
Let us continue this discussion in chat.Mistrust
E
4

TypeScript 3.4 introduces support for type-checking ECMAScript’s new globalThis.

var x = 1
const y = 2
let z = 3
globalThis.x // ok
globalThis.y // should error, no property 'y'
globalThis.z // should error, no property 'z'
globalThis['x'] // ok
globalThis['y'] // should error, no property 'y'
globalThis['z'] // should error, no property 'z'
globalThis.Float64Array // ok
globalThis.Infinity // ok

declare let test1: (typeof globalThis)['x'] // ok
declare let test2: (typeof globalThis)['y'] // error
declare let test3: (typeof globalThis)['z'] // error
declare let themAll: keyof typeof globalThis

You can ready more about it in the documentation.

Eliaeliades answered 26/5, 2020 at 21:12 Comment(1)
globalThis seems to be the future, but I haven't found a way yet on how to extend the globalThis with a typed property using TypeScript. I can do that for the Window interface and window object, but for globalThis I'm still searching for a solution.Skewer
M
3

I finally ended up with the following solution in global.d.ts

interface Window {
  foo: string
}

declare let foo: Window["foo"];

And used it in another file like

window.foo = "The value of foo"

Mistrust answered 24/12, 2019 at 10:39 Comment(2)
any idea on how to use the same approach if you want to give the foo property a custom type? As custom types cannot be imported into the global.d.ts file I'm still looking for a solution to declare the extension of the Window/GlobalThis interface with a property of a custom complex type.Skewer
I don't really know about this issue. But could this https://mcmap.net/q/47767/-how-to-import-external-type-into-global-d-ts-file-duplicate be a possible solution?Mistrust
P
3

Found a way. Perhaps a decent workaround

In a "global.d.ts" file at the root of your project, write the following

declare global {    
    var [propertyYouWantToAdd]: any; or typeof "anything you want. Perhaps import a class from a module";

//Make sure you use var instead of let or const, as it attaches to the global object.     
}

Here's the reference in the docs

Plashy answered 18/3, 2021 at 16:4 Comment(1)
Actually, this is the only way that works for me (in TS 4.3) to make a property/variable known in the global context (without prefix), in window and globalThis at the same time. Just the example code is very confusing. You don't need brackets ([]). Also, any and typeof are very uncommon ways to declare a type. It should be something like var myVar: string;, or var myObj: SomeCustomInterfaceOrType;.Fouquet
E
3

This is what worked for me using typescript version 4.5.4,

  1. Create a new .d.ts file with the needed types:
export declare global {
    var foo: string;
    var goo: string;
}
  1. Add this file to tsconfig.json file (using files property)
Elidaelidad answered 8/3, 2023 at 8:53 Comment(0)
G
1

but if you find alternative to

window.foo = 'value of foo' 

then a simple solution is just

In typescript

other_var_name is the var holding other_var_value or an object or function

const foo = 'value of foo';
(global as any).foo;

work for me

Gracioso answered 28/4, 2021 at 9:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.