Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make TypeScript output valid ES6 module import statements?

All major browsers have supported ES6 modules for some time.

These differ from many of the server-side approaches in that they need to specify the exact file to import from - they can't use file discovery.

This makes sense - in Node applications or bundlers like WebPack they only really need the name of the module, and then can spend a bit of extra time discovering the specific file that holds the code. On the web that could be a lot of wasted round trips (is 'library' in library/index.js, or library/library.js, or library.js? require() doesn't care but on the web we have to).

TypeScript has ES6 modules support (set "module": "es6" in tsconfig.json) but it appears to be using a file discovery approach...

Suppose I have library.ts:

export function myFunction(...) {  ... }

Then in app.ts:

import {myFunction} from './library';
var x = myFunction(...);

However, this is unchanged when transpiles - the TS output still has the 'library' name for file discovery, which doesn't work. This throws an error because 'library' isn't found:

<script type="module" src="app.js"></script>

In order for ES6 modules to work the TS output needs to reference the specific file:

import {myFunction} from './library.js';
var x = myFunction(...);

How do I make TS output valid ES6 module import statements?

Note: I am not asking how to make a bundler join the TS output into a single file. I specifically want to load these files individually using <script type="module">

like image 787
Keith Avatar asked Aug 29 '17 06:08

Keith


People also ask

How do I write an import statement in TypeScript?

Use import myFunction from "./myModule" to bring it in. More commonly, TypeScript modules say export myFunction in which case myFunction will be one of the properties on the exported object. Use import { myFunction } from "./myModule" to bring it in.

How do I use ES module in TypeScript?

Import ES Module In the same "src" folder create a new file named "index. ts" this will be the main entry point for our ES Module code. Once that file is created add in this TypeScript code to import the helper function that was created in the previous step. import { log } from "./helpers.

How do I import a custom module in TypeScript?

Approach: Before importing any module we need to export it from another file. We can create a module by using the export keyword and can use it in other modules by using the import keyword. We can export both class-based modules and function-based modules. as shown below.


2 Answers

This is a bug in TypeScript, though there's some debate about whether it should be fixed.

There is a workaround: while TS won't allow you to specify a .ts file as the source of a module, it will let you specify a .js extension (and then ignore it).

So in app.ts:

import {myFunction} from './library.js';
var x = myFunction(...);

This then outputs correctly in app.js, and TS has found the import definitions and bindings correctly.

This has one advantage/gotcha to be aware/careful of: TS just ignores the .js extension and loads the rest of the path with the usual file discovery. This means that it will import library.ts, but it would also find definition files like library.d.ts or import files in a library/ folder.

That last case might be desirable if you're joining those files together into a library.js output, but to do that you're going to be looking at either lots of nested tsconfig.json files (messy) or possibly the pre-transpiled output of another library.

like image 108
Keith Avatar answered Oct 09 '22 14:10

Keith


The compiler takes a module kind flag:

--module ES2015

And you'll also need to be targeting ECMAScript 6 / 2015...

--target ES2015

You need both the module kind and the compilation target to be ECMAScript 2015 minimum to have "zero transformation imports".

Your import statements should look half-way between your two examples:

import {myFunction} from './library';

Additional Notes

There is still clearly a lot of discussion about module resolution... there is the TC39 specification, and the WHATWG specification - plus Node is currently still file-extention-less... looks like RequireJS might live longer than we all thought... please see:

The TypeScript thread for supporting file extensions during import transpilation (i.e. will it add the file extension?).

Recommendation

Stick with a module loader, for example RequireJS or SystemJS. This also means your modules can be shared between browser and server by using UMD or System module kinds repectively.

Obviously, once the ECMAScript discussion reaches a conclusion this will need a revisit.

like image 44
Fenton Avatar answered Oct 09 '22 15:10

Fenton