I have some TypeScript files:
MyClass.ts
class MyClass { constructor() { } } export = MyClass;
MyFunc.ts
function fn() { return 0; } export = fn;
MyConsumer.ts
import * as MC from './MyClass'; import * as fn from './MyFunc'; fn();
This gives me errors when trying to use new
Module "MyClass" resolves to a non-module entity and cannot be imported using this construct.
and when trying to call fn()
Cannot invoke an expression whose type lacks a call signature.
What gives?
import * as MC from './MyClass';
This is ES6/ES2015-style import
syntax. The exact meaning of this is "Take the module namespace object loaded from ./MyClass
and use it locally as MC
". Notably, the "module namespace object" consists only of a plain object with properties. An ES6 module object cannot be invoked as a function or with new
.
To say it again: An ES6 module namespace object cannot be invoked as a function or with new
.
The thing you import
using * as X
from a module is defined to only have properties. In downleveled CommonJS this might not be fully respected, but TypeScript is telling you what the behavior defined by the standard is.
You'll need to use the CommonJS-style import syntax to use this module:
import MC = require('./MyClass');
If you control both modules, you can use export default
instead:
MyClass.ts
export default class MyClass { constructor() { } }
MyConsumer.ts
import MC from './MyClass';
It would have been nice to use ES6 import syntax, but now I have to do this import MC = require('./MyClass');
thing? It's so 2013! Lame! But grief is a normal part of programming. Please jump to stage five in the Kübler-Ross model: Acceptance.
TypeScript here is telling you this doesn't work, because it doesn't work. There are hacks (adding a namespace
declaration to MyClass
is a popular way to pretend this works), and they might work today in your particular downleveling module bundler (e.g. rollup), but this is illusory. There aren't any ES6 module implementations in the wild yet, but that won't be true forever.
Picture your future self, trying to run on a neato native ES6 module implementation and finding that you've set yourself up for major failure by trying to use ES6 syntax to do something that ES6 explicitly doesn't do.
Maybe you have a module loader that "helpfully" creates default
exports when none exist. I mean, people make standards for a reason, but ignoring standards is fun sometimes and we can think that's a cool thing to do.
Change MyConsumer.ts to:
import A from './a';
And specify the allowSyntheticDefaultImports
command-line or tsconfig.json
option.
Note that allowSyntheticDefaultImports
doesn't change the runtime behavior of your code at all. It's just a flag that tells TypeScript that your module loader creates default
exports when none exist. It won't magically make your code work in nodejs when it didn't before.
TypeScript 2.7 introduces support by emitting new helper methods: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-form-commonjs-modules-with---esmoduleinterop
So in tsconfig.json add these two settings:
{ // Enable support for importing CommonJS modules targeting es6 modules "esModuleInterop": true, // When using above interop will get missing default export error from type check since // modules use "export =" instead of "export default", enable this to ignore errors. "allowSyntheticDefaultImports": true }
And now you can use:
import MyClass from './MyClass';
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With