Skip to content

Can't use typeof for an ambient external module type. #2346

Closed

Description

@rotemdan

I need to get type information for a CommonJS module that's dynamically (and sometimes conditionally) loaded within the scope of an internal module. Some background on why this is important (and the hoops I go through to work around this restriction today) here: #2327 (comment)

(TL;DR: my current workaround is to transform the external module declaration file to an internal module declaration file, using several regular expressions, here's one I did for node.d.ts (with the RegExps I used in the comments): https://github.com/rotemdan/lzutf8.js/blob/master/LZUTF8/Library/Dependencies/node-internal.d.ts)

This works (with correct intellisense):

declare module InternalModule
{
    export var member: number;
}

module Test
{
    var internalModuleInstance: typeof InternalModule = SomeInternalModule;
}

But this:

declare module "ExternalModule"
{
    export var member: number;
}

module Test
{
    var externalModuleInstance: typeof ExternalModule = require("SomeExternalModule");
}

or even this:

declare module "ExternalModule"
{
    export var member: number;
}

var externalModuleInstance: typeof ExternalModule = require("SomeExternalModule");

Results in

Error: Cannot find name 'ExternalModule'

Activity

  1. rotemdan commented on Mar 13, 2015

    @rotemdan
    Author

    I found this in the handbook:
    http://www.typescriptlang.org/Handbook#modules-optional-module-loading-and-other-advanced-loading-scenarios

    It says that import x = require("y") will not emit anything unless referenced in a value position. Well I assumed that using the keyword require would immediately translate to an actual call to require but apparently I was wrong? So quite unexpectedly:

    ExternalModule.d.ts:

    declare module "ExternalModule"
    {
        export var member: number;
    }

    App.ts

    import ExternalModule = require("ExternalModule");
    
    module Test
    {
          if (false)
              var externalModuleInstance: typeof ExternalModule = require("SomeExternalModule");
    }

    emits

    var Test;
    (function (Test) {
        if (false)
            var externalModuleInstance = require("SomeExternalModule");
    })(Test || (Test = {}));

    Seems like import can be used to only import the type (when only use in a type position) but if used as a value would import the module itself. I'm not sure how many TypeScript developers are actually aware of this (perhaps this feature was added, or at least documented, only recently?) as this is quite an ambiguous use of both the keyword import and require.

    I would have still found it easier to naturally apply typeof to the ambient type itself. I do realize there might be an technical issue with parsing its identifier (having external module names in quotes perhaps means they could have spaces, start with digits etc. and can't really be used as identifiers). I'm not sure if the syntax chosen is great but it least I've found a way to make it work.

  2. danquirk commented on Mar 14, 2015

    @danquirk
    Member

    Seems like import can be used to only import the type (when only use in a type position) but if used as a value would import the module itself. I'm not sure how many TypeScript developers are actually aware of this (perhaps this feature was added, or at least documented, only recently?) as this is quite an ambiguous use of both the keyword import and require.

    You're definitely not the first to be bitten by this. It is subtle but there are good reasons for it. We've recently been talking about potential alternatives to make this scenario easier/clearer.

  3. rotemdan commented on Mar 14, 2015

    @rotemdan
    Author

    Hi, thanks for your reply! It's not every day that I discover a way to do something that I thought was impossible for literally years now.

    EDIT: I opened a new issue with a proposal for an alternative syntax: #2357.

  4. locked and limited conversation to collaborators on Jun 18, 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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions