简体   繁体   中英

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">

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.

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 (ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM