2

Have a TS 4.7 library using ESM modules.

tsconfig.json:

    "target": "ES2020",
    "module": "ES2020",
    "lib": ["ES2020"], 
    "moduleResolution": "node",

package.json

"type": "module",

I have a main file with only a one silly export:

index.ts

export { Spig } from './spig';

which is compiled to:

index.js

export { Spig } from './spig';
//# sourceMappingURL=index.js.map

Problem

When I use this library from a Node CLI program (with ESM modules enabled as well), I get the following error:

Cannot find module <path>/lib/spig imported from <path>/lib/index.js

When I manually add .js in the generated index.js, the issue is gone:

export { Spig } from './spig.js';

How can I force TypeScript compiler to generate the extension, too? What am I missing here?

2
  • I assume just adding .js to the import is a problem? Commented Aug 21, 2022 at 22:11
  • I can't add .js in my typescripts, compiler error. Commented Aug 22, 2022 at 5:37

1 Answer 1

2

You cannot omit the file extension anymore in ESM module imports. The extension should be always .js/.jsx, not .ts/.tsx for a typescript file. So, in the index.ts you should add the extension to spig export like the following and every other file imported/exported if using ESM modules.:

index.ts

export { Spig } from './spig.js'; 

Also, moduleResolution should be set to Node16 or NodeNext so ESM modules work as expected.

As stated in the docs (enphasis by me):

Relative import paths need full extensions (e.g we have to write import "./foo.js" instead of import "./foo").

When a .ts file is compiled as an ES module, ECMAScript import/export syntax is left alone in the .js output; when it’s compiled as a CommonJS module, it will produce the same output you get today under module: commonjs.

This also means paths resolve differently between .ts files that are ES modules and ones that are CJS modules. For example, let’s say you have the following code today:

// ./foo.ts
export function helper() {
    // ...
}

// ./bar.ts
import { helper } from "./foo"; // only works in CJS
helper();

This code works in CommonJS modules, but will fail in ES modules because relative import paths need to use extensions. As a result, it will have to be rewritten to use the extension of the output of foo.ts - so bar.ts will instead have to import from ./foo.js.

// ./bar.ts
import { helper } from "./foo.js"; // works in ESM & CJS
helper();

This might feel a bit cumbersome at first, but TypeScript tooling like auto-imports and path completion will typically just do this for you.

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

4 Comments

Sorry, didn't understand what I have to do or change?
Yes, but this was not complete answer. "moduleResolution": "Node16" should be set for typescript. Please update your answer for completeness
Of course. I've edited the answer with this change as needed as well.
This just explains why the extension is needed, not why we can't get something to automatically do this for us, which is the question in the title, and what I'm trying to do

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.