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

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.

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 22
  • Comments: 17 (12 by maintainers)

Commits related to this issue

Most upvoted comments

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'

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.

@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).

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

declare module 'nedb-core' {
	class Datastore {
		constructor(options?: {})
	}
	namespace Datastore {

	}
	export = Datastore
}

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.

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.

@mhegazy I agree, I’m only proposing that there be an experimental flag that enabled emitting CommonJS/AMD/etc imports when using ES6 modules. This would enable whoever is trying to use ES6 modules today to use CommonJS modules without the namespace X {} hack.

The request wasn’t about the other direction of supporting ES6 imports of CommonJS, I know that’s an issue and already something the TypeScript team will likely need to change with the latest node proposals. This proposal was only for using the legacy import x = require('x') syntax when targeting ES6 modules. If a user has enabled this proposal, it’s hopeful that they know what they’re doing. If it’s decided by the TypeScript team that this feature isn’t needed, perhaps the namespace {} hacks should stop being merged which enable the behaviour you describe (just look at the linked issues to this one).

Reference on current node proposal - https://github.com/nodejs/node-eps/pull/39. Looks like the current way of typing and importing CommonJS modules may be invalid using this proposal.

@mhegazy Have you considered enabling a flag for CommonJS interop in ES6 modules? It’s inevitable that people will want to use CommonJS requires and have ES6 exports for a period of time.

Edit: To clarify, in case that didn’t make sense, a flag that allows import x = require('x') syntax when using ES6 exports. Having every node module update now, or into the future, seems pretty unfeasible so even people that will adopt ES6 (now with their bundler or in the future with native support) will require to continue using import x = require('x') for “legacy” external modules.