218

I am using TypeScript 1.6 with ES6 modules syntax.

My files are:

test.ts:

module App {
  export class SomeClass {
    getName(): string {
      return 'name';
    }
  }
}

main.ts:

import App from './test';

var a = new App.SomeClass();

When I am trying to compile the main.ts file I get this error:

Error TS2306: File 'test.ts' is not a module.

How can I accomplish that?

2
  • I had this issue , I did not have a constructor in the class , added one and problem went away Commented May 22, 2019 at 12:00
  • I was simply importing the wrong file... Commented Mar 27, 2021 at 11:27

10 Answers 10

203

Extended - to provide more details based on some comments

The error

Error TS2306: File 'test.ts' is not a module.

Comes from the fact described here http://exploringjs.com/es6/ch_modules.html

17. Modules

This chapter explains how the built-in modules work in ECMAScript 6.

17.1 Overview

In ECMAScript 6, modules are stored in files. There is exactly one module per file and one file per module. You have two ways of exporting things from a module. These two ways can be mixed, but it is usually better to use them separately.

17.1.1 Multiple named exports

There can be multiple named exports:

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}
...

17.1.2 Single default export

There can be a single default export. For example, a function:

//------ myFunc.js ------
export default function () { ··· } // no semicolon!

Based on the above we need the export, as a part of the test.js file. Let's adjust the content of it like this:

// test.js - exporting es6
export module App {
  export class SomeClass {
    getName(): string {
      return 'name';
    }
  }
  export class OtherClass {
    getName(): string {
      return 'name';
    }
  }
}

And now we can import it in these three ways:

import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";

And we can consume imported stuff like this:

var a1: app1.App.SomeClass  = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();

var b1: app2.App.SomeClass  = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();

var c1: App.SomeClass  = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();

and call the method to see it in action:

console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())

Original part is trying to help to reduce the amount of complexity in usage of the namespace

Original part:

I would really strongly suggest to check this Q & A:

How do I use namespaces with TypeScript external modules?

Let me cite the first sentence:

Do not use "namespaces" in external modules.

Don't do this.

Seriously. Stop.

...

In this case, we just do not need module inside of test.ts. This could be the content of it adjusted test.ts:

export class SomeClass
{
    getName(): string
    {
        return 'name';
    }
}

Read more here

Export =

In the previous example, when we consumed each validator, each module only exported one value. In cases like this, it's cumbersome to work with these symbols through their qualified name when a single identifier would do just as well.

The export = syntax specifies a single object that is exported from the module. This can be a class, interface, module, function, or enum. When imported, the exported symbol is consumed directly and is not qualified by any name.

we can later consume it like this:

import App = require('./test');

var sc: App.SomeClass = new App.SomeClass();

sc.getName();

Read more here:

Optional Module Loading and Other Advanced Loading Scenarios

In some cases, you may want to only load a module under some conditions. In TypeScript, we can use the pattern shown below to implement this and other advanced loading scenarios to directly invoke the module loaders without losing type safety.

The compiler detects whether each module is used in the emitted JavaScript. For modules that are only used as part of the type system, no require calls are emitted. This culling of unused references is a good performance optimization, and also allows for optional loading of those modules.

The core idea of the pattern is that the import id = require('...') statement gives us access to the types exposed by the external module. The module loader is invoked (through require) dynamically, as shown in the if blocks below. This leverages the reference-culling optimization so that the module is only loaded when needed. For this pattern to work, it's important that the symbol defined via import is only used in type positions (i.e. never in a position that would be emitted into the JavaScript).

Sign up to request clarification or add additional context in comments.

7 Comments

But this: import App = require('./test'); is not the syntax of es6 modules. this is common js. Can i do it with es6 modules syntax?
@JsIsAwesome You're trying to mix JS modules with Typescript modules. You need to use one or the other, not a mixture of both.
This answer does not refer to the ES6 syntax
@phiresky, what do you mean?
The examples use import x = require('test') while ES6 module syntax looks like import x from "test"
|
73

Above answers are correct. But just in case... Got same error in VS Code. Had to re-save/recompile file that was throwing error.

6 Comments

This worked for me. I simply removed a semi-colon, re-added it and saved the file again, and then running Webpack worked. Great time to be alive.
I'm used to Webstorm and didn't realize files aren't saved automatically in VS Code. This answer saved me a lot of pain, thanks.
There's a setting for auto save in VS Code. I don't use it because VS Code already backs up unsaved files and I don't always use git.
awesome! I closed the file and open it again and the problem was solved, it's related to change the filename while it's opened I guess.
On a similar note, running Developer: Reload Window from the command line fixed it for me.
|
17

How can I accomplish that?

Your example declares a TypeScript < 1.5 internal module, which is now called a namespace. The old module App {} syntax is now equivalent to namespace App {}. As a result, the following works:

// test.ts
export namespace App {
    export class SomeClass {
        getName(): string {
            return 'name';
        }
    }
}

// main.ts
import { App } from './test';
var a = new App.SomeClass();

That being said...

Try to avoid exporting namespaces and instead export modules (which were previously called external modules). If needs be you can use a namespace on import with the namespace import pattern like this:

// test.ts
export class SomeClass {
    getName(): string {
        return 'name';
    }
}

// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();

1 Comment

It is still a good practice? According to this answer (stackoverflow.com/a/35706271/2021224), attempting to import a function or class like this and then invoke it - "is illegal according to the ES6 spec".
14

In addition to A. Tim's answer there are times when even that doesn't work, so you need to:

  1. Rewrite the import string, using the intellisense. Sometimes this fixes the issue
  2. Restart VS Code

2 Comments

same for stackblitz - recompiled file that imports the module and all works fine, cheers
I also experienced this when my code wasn't formatted right. VSCode indented my copy+paste class code when I was splitting my classes into their own files, and VSCode indented everything after the export class... {, which angular did not like, giving me this issue. After fixing the formatting, compiled without an issue.
4

I had this issue and I had forgotten to export the Class.

1 Comment

Similarly silly: I'd forgotten to save the new file. Check for that too, people.
2

Just in case this may works for you as it did form me, i had this files

//server.ts

class Server{
...
}

exports.Server = Server
//app.ts

import {Server} from './server.ts'

And this actually raised an error but i changed server.ts to

//server.ts

export class Server{
...
}

and it worked 😎👌

Note: i am using this config

 "target": "esnext",
 "module": "commonjs",

1 Comment

Thanks but doesn't work with svelte since it has its own declarations for built in features
2

I faced the same issue in a module that has no exports. I used it for side-effects only. This is what the TypeScript docs say about importing side-effects modules:

Though not recommended practice, some modules set up some global state that can be used by other modules. These modules may not have any exports, or the consumer is not interested in any of their exports. To import these modules, use:

import "./my-module.js";

In that situation, you can fix the "File is not a module" error by simply exporting an empty object:

// side-effects stuff

export default {};

Comments

1

In addition to Tim's answer, this issue occurred for me when I was splitting up a refactoring a file, splitting it up into their own files.

VSCode, for some reason, indented parts of my [class] code, which caused this issue. This was hard to notice at first, but after I realised the code was indented, I formatted the code and the issue disappeared.

for example, everything after the first line of the Class definition was auto-indented during the paste.

export class MyClass extends Something<string> {
    public blah: string = null;

    constructor() { ... }
  }

Comments

1

I faced the same issue ("File is not a module error") for import js in vue component

import handleClientLoad from "../../../public/js/calendar.js"

I do this and solve it

// @ts-ignore
import handleClientLoad from "../../../public/js/calendar.js"

Comments

1

Empty File

Although not in your case, but for other viewers, I had the same issue after creating a new .ts file. It turns out an empty file is not a considered module. You can simply fill it, and export something to make it a module.

You can just write:

export { };

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.