Skip to content

Global type references #983

Description

When a type declaration in an inner module has the same name as a type declaration in an outer module, the outer declaration becomes inaccessible. Programmers can typically work around this, for example by deriving a type with an alternate name in the outer module or, with our latest changes, by declaring a type alias. However, such manual work-arounds are not a good option for code generation tools such as the .d.ts generation facility provided by the compiler. We really need a fool proof way to ensure a lookup starts at the global scope. Consider:

function makeError(): Error {
    return new Error("We've got a problem");
}

module M {
    export class Error {
    }
    export var x = makeError();  // No way to name global Error type here
}

It is currently impossible to create a .d.ts file for the code above because there is no way to emit a type annotation for x that references the global interface Error.

Node.js uses the name global for the global scope. We could consider supporting something similar in type names. Specifically, if a qualified type name starts with the identifier global and if a lookup of global as a module name doesn't bind to anything (meaning the user didn't define a global module), then we consider it a globally qualified name. For the example above we could then generate the following .d.ts file:

declare module M {
    class Error {
    }
    var x: global.Error;
}

If course, if the user defines their own global module we now have a problem again--but I think it is fine to just say you shouldn't do that. We could even consider making it an error, or allowing it only under a compiler switch.

Activity

  1. added this to the milestone on Oct 28, 2014
  2. mhegazy commented on Oct 31, 2014

    @mhegazy
    Contributor

    how about a global modifier instead of reserving the name something like ``global::`:

    declare module M {
        export class Error {
        }
        var x: global::Error;
    }
    var e: global::M.Error;
  3. DanielRosenwasser commented on Nov 1, 2014

    @DanielRosenwasser
    Member

    global:: is interesting. C++ has ::x to retrieve an x in the global namespace, so it sort of reminds me of that.

  4. RyanCavanaugh commented on Nov 3, 2014

    @RyanCavanaugh
    Member

    We just as much need to solve the problem for external modules, so we'd need another special identifier that meant "the containing external module". global makes sense for internal modules but I think we'll have a hard time finding a non-conflicting equivalent for external modules. Just using a different syntax seems like a cleaner solution at that point.

  5. sccolbert commented on Nov 20, 2014

    @sccolbert

    👍 for a way to explicitly resolve global scope

  6. basarat commented on Dec 11, 2014

    @basarat
    Contributor

    It is possible with a bit of _ab_use of declare var and typeof:

    function makeError(): Error {
        return new Error("We've got a problem");
    }
    
    declare var GlobalError:Error;
    
    module M {
        export class Error {
        }
        export var x:typeof GlobalError = makeError();  // captured global type Error
    }

    Related stackoverflow question: http://stackoverflow.com/a/27433864/390330

  7. basarat commented on Jan 13, 2015

    @basarat
    Contributor

    I could get behind either global::foo or ::foo

    👍 for ::foo

    Reason: I don't like the confusion global will create with respect to something actually called global e.g. http://nodejs.org/api/globals.html#globals_global

  8. mhegazy commented on Jan 14, 2015

    @mhegazy
    Contributor

    The main objection to :: is that it looks bad in a type position:
    var x: ::Error;

  9. cseufert commented on Feb 16, 2015

    @cseufert

    Could we just use a leading . so .File or .Error, in the vein of PHP with \BuiltInClass.

    Alternatively, when in a browser context, use window.File, or window.Error, since whats were they are declared, rather than adding a new syntax.

  10. basarat commented on Feb 16, 2015

    @basarat
    Contributor

    when in a browser context

    They/I would really like to support the same syntax for browser/node/anything else. Runtime shouldn't define TypeScript semantics (or as little as possible).

    I don't see a reason to not use . though (although there very well might be a technical reason out there).

  11. cseufert commented on Feb 16, 2015

    @cseufert

    I guess the problem with using window.X, global.X or any other . is that there is always the possibility of someone clobbering the namespace, with a parent module. Especially when these modules can be declared in another file. (Just tested this is the case).

  12. basarat commented on Feb 16, 2015

    @basarat
    Contributor

    global:: 👍

  13. 26 remaining items

  14. mhegazy commented on Mar 1, 2016

    @mhegazy
    Contributor

    But on 1.8 I get this error: error TS2661: Cannot re-export name that is not defined in the module..

    this is in accordance with the ES6 spec. see https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#exporting-non-local-names-from-a-module for more details.

    To work around this, you need to have a local declaration, e.g:

    const localTouch = Touch;
    export {Touch as BrowserTouch};
  15. bartzy commented on Mar 2, 2016

    @bartzy

    Mohamed Hegazy (@mhegazy) Thanks.

    But now when I import it, I get this error: error TS2304: Cannot find name 'BrowserTouchEvent'.

    This is my import line:

    import {BrowserTouch, BrowserTouchEvent} from './BrowserTypes';
    

    And this is BrowserTypes.ts:

    const localTouch = Touch;
    const localTouchEvent = TouchEvent;
    
    export {localTouch as BrowserTouch, localTouchEvent as BrowserTouchEvent};
    

    I also tried:

    const BrowserTouch = Touch;
    const BrowserTouchEvent = TouchEvent;
    export {BrowserTouch, BrowserTouchEvent};
    

    Thanks.

  16. mhegazy commented on Mar 2, 2016

    @mhegazy
    Contributor

    Bar Ziony (@bartzy), I can not get a repro for your scenario.

    c:\test\983>type BrowserTypes.ts
    const localTouch = Touch;
    const localTouchEvent = TouchEvent;
    
    export {localTouch as BrowserTouch, localTouchEvent as BrowserTouchEvent};
    
    c:\test\983>type app.ts
    import {BrowserTouch, BrowserTouchEvent} from './BrowserTypes';
    
    c:\test\983>tsc --v
    Version 1.8.7
    
    c:\test\983>tsc --m commonjs app.ts
    
    c:\test\983>echo %ERRORLEVEL%
    0
  17. bartzy commented on Mar 2, 2016

    @bartzy

    Mohamed Hegazy (@mhegazy), Try to use BrowserTouchEvent as a type to an argument in a function in app.ts.

    I get error TS2304: Cannot find name 'BrowserTouchEvent'. for that.

  18. mhegazy commented on Mar 2, 2016

    @mhegazy
    Contributor

    aah, it is not a type, it is a value.. see https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Declaration%20Merging.md#basic-concepts for more information..

    What you want is to export both:

    const localTouchEvent = TouchEvent; // only value
    type  localTouchEvent  = TouchEvent; // only type
    
    export {localTouchEvent as BrowserTouchEvent}; // export both value and type

    ideally you would use import localTouchEvent = TouchEvent which will get you "all" meanings of TouchEvent, but this is not supported at the moment, see #1166

  19. bartzy commented on Mar 2, 2016

    @bartzy

    Thanks, it works!

    I was under the impression that const localTouchEvent = TouchEvent does capture the type as well.
    I also didn't get what syntax is import localTouchEvent = TouchEvent? It's not ES6 syntax, right?

  20. mhegazy commented on Mar 2, 2016

    @mhegazy
    Contributor

    no, but types are not an ES6 either.

  21. rjamesnw commented on Apr 5, 2017

    @rjamesnw

    Just checking - is this still an issue, or is there a solution to this yet? I used to have issues access types using some kind of fully qualified naming approach (as Anders suggested), but not sure where things stand now after much time has passed.

  22. mnpenner commented on Aug 3, 2017

    @mnpenner

    Yeah, where are we at with this? Is there a way to reference a global that collides with something inside the namespace or no?

  23. fly-studio commented on Dec 6, 2017

    @fly-studio

    As this problem

    // a 3rd lib
    namespace eui {
        class IAssetAdapter {}
    }
    //My lib
    namespace layer.eui {
    
       // the eui below, is not a global namespace,
       // it defines to layer.eui.IAssetAdapter
       class AssetAdapter extends eui.IAssetAdapter {  
       }
    }
    

    I known how to close this error, like this, but it'll export a global variant euiIAssetAdapter

    import euiIAssetAdapter = eui.IAssetAdapter
    namespace layer.eui {
      class XXX entends euiIAssetAdapter  {
     }
    }
    

    Do this have a root namespace symbol, like PHP \eui\IAssetAdapter or ::eui.IAssetAdapter

  24. inad9300 commented on May 12, 2018

    @inad9300

    Any progress on this front?

  25. locked and limited conversation to collaborators on Jul 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions