Nickey
Nickey

Reputation: 1381

Using globalThis in Typescript

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

Upvotes: 56

Views: 73098

Answers (9)

arielhad
arielhad

Reputation: 2163

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)

Upvotes: 4

Andreas Linnert
Andreas Linnert

Reputation: 577

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.

Upvotes: 27

Christopher Peisert
Christopher Peisert

Reputation: 24194

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.

Upvotes: 97

Ali Virk
Ali Virk

Reputation: 49

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

Upvotes: 1

Edward Casanova
Edward Casanova

Reputation: 1064

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

Upvotes: 3

Chase Holland
Chase Holland

Reputation: 2268

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' };

Upvotes: 13

Melchia
Melchia

Reputation: 24314

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.

Upvotes: 4

Nickey
Nickey

Reputation: 1381

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"

Upvotes: 3

C.OG
C.OG

Reputation: 6529

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

Upvotes: 5

Related Questions