Unknown file extension ".ts" error in ts-node [Solved]

avatar
Borislav Hadzhiev

Last updated: Feb 29, 2024
5 min

banner

# Unknown file extension ".ts" error in ts-node [Solved]

The ts-node error '[ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"' occurs when the type property is set to module in your package.json file.

You can solve the error, issue the ts-node command with the --esm flag or remove the "type": "module" property from your package.json file.

typeerror err unknown file extension ts

Here is an example of how the error occurs.

Notice that the type attribute is set to module in package.json.

package.json
{ "type": "module", "name": "bobbyhadz-ts", "scripts": { "dev": "ts-node src/index.ts" }, "devDependencies": { "@types/node": "^18.15.5", "ts-node": "^10.9.1", "typescript": "^5.0.2" } }

If I try to issue the npx ts-node my-file.ts command, the error is raised.

shell
# ⛔️ TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /home/borislav/Desktop/bobbyhadz-ts/src/index.ts npx ts-node my-file.ts

how the error is caused

# Use the --esm flag when issuing the ts-node command

One way to solve the error is to use the --esm flag when issuing the ts-node command.

shell
# ✅ works npx ts-node --esm my-file.ts

set esm flag when issuing ts node command

The code for this article is available on GitHub

You can define a dev script in your package.json file to not have to remember to set the --esm flag every time.

package.json
{ "type": "module", "scripts": { "dev": "ts-node --esm my-file.ts" }, "devDependencies": { "@types/node": "^18.15.5", "ts-node": "^10.9.1", "typescript": "^5.0.2" } }
Replace the my-file.ts placeholder with the path to your entry .ts file.

Make sure you have ts-node installed and issue the npm run dev command instead.

shell
# 👇️ install with NPM npm install ts-node --save-dev # 👇️ or with YARN yarn add ts-node --dev

After installing ts-node, run the command with npm run dev.

shell
npm run dev

using npm run script instead

The --esm flag is used to enable the ESM (ES Modules) loader.

The ES Modules loader enables you to use the ES6 import/export syntax with ts-node.

The ES Modules loader can also be enabled with the ts-node-esm command.

shell
npx ts-node-esm src/index.ts bobbyhadz.com

using ts node esm command

This is equivalent to using the --loader flag explicitly.

shell
node --loader ts-node/esm my-file.ts

node typescript custom esm loader

Depending on your Node.js version, you might get a message that custom ESM loaders is an experimental feature.

# Enable the ES Modules loader in tsconfig.json

You can also enable the feature by editing your tsconfig.json file.

This allows you to issue the ts-node command without setting the --esm flag.

  1. set the esm property to true
  2. set the experimentalSpecifierResolution property to node.
tsconfig.json
{ // 👇️ set these 2 properties inside the ts-node object "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" }, "compilerOptions": { // ... rest } }

set esm to true in tsconfig json

The code for this article is available on GitHub

You don't have to use the --esm flag after you enable the feature in your tsconfig.json file.

shell
# ✅ Works npx ts-node my-file.ts

using npm run script instead

If the error persists, make sure you have the following properties set in the compilerOptions object in your tsconfig.json file.

tsconfig.json
{ "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" }, "compilerOptions": { "esModuleInterop": true, "moduleResolution": "node", "module": "CommonJS", // ... rest } // ... rest }

Setting the esModuleInterop property to true is very important.

The esModuleInterop option is set to false by default, which causes it to treat CommonJS modules similar to ES6 modules. This causes some issues.

Setting esModuleInterop to true fixes these issues.

The compilerOptions.module property is used to specify what module code is generated.

The property can be set to ESNext to enable additional APIs that are supported by your TypeScript version.

tsconfig.json
{ // ... rest "compilerOptions": { "module": "ESNext", // ... rest } // ... rest }
The code for this article is available on GitHub

# Remove "type": "module" from your package.json file

You can also solve the error by removing the type property from your package.json file.

package.json
{ "type": "module", // 👈️ remove this line "name": "bobbyhadz-ts", "scripts": { "dev": "ts-node src/index.ts" }, "devDependencies": { "@types/node": "^18.15.5", "ts-node": "^10.9.1", "typescript": "^5.0.2" } }

When the type property is set to module, all .js files in the project are treated as ES modules.

This can cause issues when using ts-node as ES Modules have not yet been fully integrated.

Once you remove the type property, you can issue the ts-node my-file.ts command without getting the error.

shell
npx ts-node my-file.ts

ts node command works

If the type field is omitted or set to commonjs, all .js files are treated as CommonJS.

If you don't want to prefix ts-node commands with npx, install the module globally.

shell
npm install -g ts-node

Now the ts-node command can be used directly.

shell
ts-node my-file.ts
The code for this article is available on GitHub

# Enabling the ESM loader globally with an environment variable

If removing the type attribute from your package.json file causes different errors, add the attribute back.

package.json
{ "type": "module", // 👈️ add this line back "name": "bobbyhadz-ts", "scripts": { "dev": "ts-node src/index.ts" }, "devDependencies": { "@types/node": "^18.15.5", "ts-node": "^10.9.1", "typescript": "^5.0.2" } }

Depending on your setup, you might have to set the NODE_OPTIONS environment variable to --loader ts-node/esm to enable the ES Modules loader feature.

The easiest way to set the environment variable is to do it right before issuing the ts-node command.

shell
# for macOS, Linux, Git Bash NODE_OPTIONS="--loader ts-node/esm" node my-file.ts

enable esm with environment variable

However, if you run the command on Windows in CMD or PowerShell, you will get an error.

You can use the cross-env package to be able to run the command on all operating systems.

First, install the module by running the following command.

shell
# 👇️ with NPM npm install cross-env # 👇️ with YARN yarn add cross-env

Now, set the command as a script in your package.json file.

package.json
{ "scripts": { "dev": "NODE_OPTIONS=\"--loader ts-node/esm\" node my-file.ts" } }

Notice that we had to escape the double quotes with backslashes.

You can now run the command with npm run dev.

shell
npm run dev

npm run dev with esm enabled

Now the command is run with the ESM loader enabled.

# Using tsc and node instead of ts-node

If the error persists, try to use the tsc and node commands manually instead of using ts-node (which combines the two).

shell
npx tsc --outDir dist my-file.ts node dist/my-file.js

using tsc and node commands separately

The tsc command is used to convert the .ts file to a .js file.

The file is placed in the specified --outDir (dist in the example).

The last step is to use the node command to run the .js file.

You can also use an ampersand && to combine the two commands.

shell
npx tsc --outDir dist my-file.ts && node dist/my-file.js

combine tsc and node commands

The .js file is located in the specified --outDir.

The code for this article is available on GitHub

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.