microsoft/TypeScript

Access `global` as a type

blakeembrey opened this issue ยท 9 comments

Since https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#augmenting-globalmodule-scope-from-modules it's been possible to augment the global from a module. However, in TypeScript definitions (such as dom.d.ts or node.d.ts) we're left to create our own "globals". This results in two issue:

  1. inconsistency with what is actually the global namespace
  2. requiring the declaration of global types multiple times in case it's used as a "real global" or from window. (browsers) or global. (node)

Instead of this, it'd be really nice to access TypeScript's global as a type. For instance, in dom.d.ts we could do:

declare var window: global

Or in node.d.ts:

declare var global: global

I couldn't see any previous issues, but it's partially related to things like #12902 - the difference though is that those issues seems to be tracking adding variables to the TypeScript global scope whereas this would be using the global scope as a type to define these variables themselves.

This is similar to #10050. Having a referenceable global type would do away with some painful duplication in declaration files. An additional scenario is sandboxed globals, described in #10050 (comment).

I would say this should be covered by #12902

@mhegazy Sorry, I read #12902 (comment) as a comment meaning it was really just a lib.d.ts change (e.g. declare var global: Window). If it is instead adding a real global type that mirrors TypeScript's "global" state and can be used in a type position for module authors I'm happy to close this issue.

@mhegazy Sorry, I read #12902 (comment) as a comment meaning it was really just a lib.d.ts change (e.g. declare var global: Window).

I think this is the feeling when we looked at this first. but I would expect users will have a similar request like yours. i would expect ppl wanting to do typeof global and be able to get things in declare global {...} blocks as well as declarations in the global scope.

If it is instead adding a real global type that mirrors TypeScript's "global" state and can be used in a type position for module authors I'm happy to close this issue.

I am fine lumping the two together, or keeping them separate. I would expect we need to do both if the proposal moves forward in the committee.

We just ran in to this as well. We are handcoding typescript definitions for a legacy js library in the hope of pushing it to @types. We have multiple internal modules. One of the modules have a global variable X. And other have a variable X inside our common namespace Y. Now when the d.ts file of a third module want to refer the global X from inside our common namespace Y, there is no way right now. Any help on this?

@nolakara the need for a way to fully qualify a name has been discussed previously in #983. we then concluded that there was not a pressing need for this with modules and aliasing support. The recommendation for this case is to alias the global variable outside of your namespace and use the alias instead, e.g.:

import global_x = x;
namespace Y {
    export class x { }
   
   var local: x;
   var global: global_x;
}

One way I resolved this was to create a layer from global to register globals. In namespace "A" I register global "x". "y". "z" properties (i.e. namespace A { export var x: ExpectedType; } A.x = x;), or even interfaces IX, IY, IZ, etc. In namespace A.B, or A.B.C, etc.,, I register all other types. This means I can always access A.x or A.IX (globals), or A.B.x (local types), etc. To make it easier I created a separate namespace for the globals, such as namespace AGlobals { export var globalx = x; } then you could do namespace A { export var x = AGlobals.globalx; }.

Example: https://goo.gl/JreGBg

var x = 1;
interface ISomeType { y: number; }

namespace AGlobals {
    export var globalx = x;
    export type globalISomeType = ISomeType;
}

namespace A {
    export var x = x;
    export type ISomeType = AGlobals.globalISomeType; // (OR use only AGlobals.globalISomeType instead)
    export namespace B {
        export var x = 2;
        export interface ISomeType { z: number; }
        var o: A.ISomeType; // (OR AGlobals.globalISomeType - depends on what you want)
        export function doSomething() {
            A.x = A.B.x;
            o.y = x;
        }
    }
}

There is another way to access globals using eval (or Function(...)) like this:

var x = 1; 
function test() { var x = 2; return eval.call(null, "x"); } 
test(); // returns 1 because eval is now called in the global scope.

Using new Function("return x;") also works instead of eval.call().

That assumes, of course, you are not trying to access a type (like an interface), and you will have to explicitly specify the expected type manually.

Random update: We pretty much know we want to do this (and talked about it when we added import types), we're just waiting for https://github.com/tc39/proposal-global (the proposal referenced in #12902) to stabilize with a name for the global-thing, so we can use the same name for the type lookup.

This is now supported by using typeof globalThis in a type position.