Skip to content

import * as alias syntax doesn't work with export = function unless merged with namespace #5073

Description

declare module "foo" {
  function foo(): void;
  export = foo;
}

Can't be included via import * as f from "foo";.
(test.ts(1,20): error TS2497: Module '"foo"' resolves to a non-module entity and cannot be imported using this construct.)
Whereas

declare module "foo" {
  function foo(): void;
  namespace foo {}
  export = foo;
}

Can be. The distinction seems artificial.

Activity

  1. mhegazy commented on Oct 2, 2015

    @mhegazy
    Contributor

    there is no way in an ES6 module to achieve this. the parallel in an ES6 module world is to default export. import * will import the namespace component of the export, if it exists, if it does not, it is an error. That you can call the alias as a function in the second case, is the bug i would say.

  2. 8 remaining items

  3. blakeembrey commented on Aug 27, 2016

    @blakeembrey
    Contributor

    what does it mean for users who would want to run this in a non-commonjs engine? e.g. ES6 enabled web browser? require does not make sense here.

    Mohamed Hegazy (@mhegazy) To follow up on that, the namespace {} hack proposed and used in every linked issue here enables the behaviour you describe. It seems there's enough people committing the hack (and having it merged) that it would warrant looking at whether you should enable import x = require('x'). If not, perhaps those PRs should start being rejected with proper guidance on how to use the import syntax in these cases (E.g. by recommending people use CommonJS modules instead of ES6 or just informing people of import x = require('x') - I've found enough people just didn't know the import x = style existed it or have tried to enforce a style without understanding the implications and then use this hack to make things work instead).

  4. unional commented on Apr 4, 2017

    @unional
    Contributor

    Without the hack, TS code cannot target es6 if it consumes cjs module.

    With Babel, JS user is doing import x from 'x' and it is working fine for them.
    For TS, right now we do not have any working solution except the hack.

  5. byrgvt commented on May 3, 2017

    @byrgvt

    I still can't tell if this was ever intended behavior or not (that using the namespace allows this to pass typecheck), or just a happy accident that now too many people rely on to change.n

    It seems an empty namespace gets SymbolType NamespaceModule (1024) and the checker looks for the merged symbol. So basically

    function C {} // symbol type 16
    namespace C {} // symbol type 1024
    (1024 | 16) & SymbolFlags.Module == true (from resolveESModuleSymbol in compiler/checker.ts)

    is what I think is happening?

    The PR that added module support #2242 explicitly mentions:

    A module that uses export = to export a non-module entity in place of the module itself must be imported using the existing import x = require("foo") syntax as is the case today.

    But I don't understand what typescript considers a non-module entity. The actual line in checker that flags these errors is:
    (!dontResolveAlias && symbol && !(symbol.flags & (SymbolFlags.Module | SymbolFlags.Variable)))

    So I can get away with pretty confusing stuff like:

    a.ts
    const x = () => 1;
    export = x;
    
    b.ts
    import * as x from './a';
    
    x()
    

    Which passes compilation fine as with ./node_modules/.bin/tsc b.ts -t "es6" -m "amd".

    Is a module entity related to https://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects, which is what the es6 grammar specifies is the type of import * as foo?

  6. added a commit that references this issue on Jun 7, 2017
  7. qm3ster commented on Dec 5, 2017

    @qm3ster

    So, is this the hack?
    And is it still the best way?

    declare module 'nedb-core' {
    	class Datastore {
    		constructor(options?: {})
    	}
    	namespace Datastore {
    
    	}
    	export = Datastore
    }
  8. LouisWayne commented on Dec 25, 2017

    @LouisWayne

    Mihail Malo (@qm3ster) I think so.. I don't like the consistency here..have you found any solution for this???

  9. qm3ster commented on Jan 3, 2018

    @qm3ster

    the L (@LouisWayne) well, the reason a hack might be the best we can do is because we are trying to ES6-import something that could never have been ES6-exported.
    I am currently doing what I wrote above and not having any problems.

  10. unional commented on Mar 24, 2018

    @unional
    Contributor

    With esModuleInterop available, you should not need this hack anymore.
    You should be able to do:

    import x from 'cjs'
    
    // instead of 
    import * as x from 'cjs'
  11. aodinok commented on Apr 2, 2018

    @aodinok

    Thanks! esModuleInterop works like a sharm!

  12. locked and limited conversation to collaborators on Jul 25, 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

    By DesignDeprecated - use "Working as Intended" or "Design Limitation" instead

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions