Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 614
Nickey Avatar asked Dec 23 '19 17:12

Nickey


Video Answer


2 Answers

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.

like image 120
Christopher Peisert Avatar answered Oct 15 '22 07:10

Christopher Peisert


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)

interface Person {
  name: string
}

export 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 this way. You don't need to export declare global if you can export something else instead:

export interface Person { /* ... */ }

declare global { /* ... */ }

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.

like image 35
Andreas Linnert Avatar answered Oct 15 '22 08:10

Andreas Linnert