TypeScript 4.7 is released π!
The wait is finally over. You are excited to start your migration.
So you update your package.json
,
update your tsconfig.json
,
and voila, you are greeted with a boat load of errors.
What you need to do is to update your import
statements:
- add
.js
extension for files, or - add
/index.js
for folders.
You can do that manually, or you can use a tool to help you on that.
The tool (tsc-esm-fix
) is the focus of the topic today.
But before that,
let me highlight a few things about the current state of the type: module
support in TypeScript 4.7,
so that you can decide should you take the jump right now,
or should you wait a little bit longer.
There are four issues that I'm aware of related to type: module
.
The .js
extension debateβ
The first one is a debate around the .js
extension.
The takeaways are:
.js
is artificial, pointing to a non-existing file- TypeScript team do not want to rewrite JS code
- NodeJS needs extra tricks to detect the source type
You can read about the detail here:
- https://github.com/microsoft/TypeScript/issues/49083
- https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937
broken source mapβ
The second one is that source map is currently broken.
If your code will be used in the browser, you need to weight that in.
You can read about the detail here: https://github.com/microsoft/TypeScript/issues/49335
ts-loader workaroundβ
The third one is that currently ts-loader
does not support ESM module out of the box.
You have to do two things.
- use the
NormalModuleReplacementPlugin
:
{
plugins: [
new NormalModuleReplacementPlugin(/.js$/, (resource) => {
if (/node_modules/.test(resource.context)) return
resource.request = resource.request.replace(/.js$/, '')
})
]
}
- Do not use
transpileOnly: true
. It does not work.
If I have time, I might dig in to help ts-loader
about this,
but not sure when I'll be able to do that.
You can read about the detail here: https://github.com/TypeStrong/ts-loader/issues/1463
jest ESM workaroundβ
The last one is an outstanding one,
that jest
does not have native support of ESM.
Meaning when you use ts-jest
, you also need to use babel-jest
to transpile dependencies within node_modules
.
Here is a nutshell of what you need to do:
// jest.config.mjs
export default {
preset: 'ts-jest/presets/default-esm',
globals: {
'ts-jest': {
useESM: true
}
},
moduleNameMapper: {
// remove the phantom `.js` extension
'^(\\.{1,2}/.*)\\.js$': '$1',
// If dependency doing `import ... from '#<pkg>'.
// e.g. `chalk` has this: `import ... form '#ansi-styles'`
'#(.*)': '<rootDir>/node_modules/$1'
},
transformIgnorePatterns: [
// Need to MANUALLY identify each ESM package, one by one
'node_modules/(?!(@unional\\fixture|chalk)/)'
],
transform: {
'^.+\\.(js|jsx|mjs)$': 'babel-jest',
}
}
You can find related information here: https://dev.to/steveruizok/jest-and-esm-cannot-use-import-statement-outside-a-module-4mmj
tsc-esm-fix
β
Now, if you decide to move ahead,
then you can use tsc-esm-fix
to help you.
I used it to help me migrating type-plus
, which has 140+ files.
I'm using it for other packages as I'm writing this blog.
I have discovered a few bugs and @antongolub is very quick to fix them. Go to the repo and give it a star β!
The easiest way to use it is through npx
or yarn dlx
:
npx tsc-esm-fix --src=<src> --ext='.js'
# or
yarn dlx tsc-esm-fix --src=<src> --ext='.js'
For type-plus
, since I put the source code under the ts
folder,
so the command is:
npx tsc-esm-fix --src=ts --ext='.js'
If you put your source code under the typical src
folder,
then the command is:
npx tsc-esm-fix --src=src --ext='.js'
Happy Coding, π§βπ»