The ErrorYou’ve likely just added "type": "module" to your package.json to enjoy modern ES Modules. However, the moment you try to run a TypeScript file, Node.js crashes. Instead of executing your code, the runtime throws a frustrating extension error:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /path/to/file.ts
at new NodeError (node:internal/errors:399:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:79:11)
Why This HappensNode.js does not support TypeScript natively. When you enable ES Modules (ESM), Node.js uses a strict loader that only recognizes .js, .mjs, and .json files. Unlike the older CommonJS system, the ESM loader won't let ts-node easily hook into the runtime without specific flags.
If you run node script.ts, Node looks at the extension and immediately stops. It simply doesn't know what to do with a .ts file in an ESM context.
Solution 1: Use 'tsx' (The Modern Standard)For development, tsx is currently the most reliable tool. It is a drop-in replacement for Node.js built on esbuild, making it up to 10x faster than traditional ts-node. It handles ESM and TypeScript with zero extra configuration.
1. Install tsx```
npm install --save-dev tsx
### 2. Run your fileSimply swap `node` or `ts-node` for `tsx`:
npx tsx ./src/index.ts
This works because `tsx` automatically registers the necessary loaders in the background. You won't have to manage complex flags or experimental warnings.
## Solution 2: Using 'ts-node' with ESM LoaderIf your workflow depends on `ts-node`, you must explicitly tell Node.js how to handle the ESM loader. The command you use depends on your specific Node.js version.
### For Node.js v20.6.0+ or v18.19.0+Recent updates introduced the `--import` flag. This is the stable, modern way to register TypeScript support:
node --import ts-node/register ./src/index.ts
### For Older Node.js Versions (v16.x or early v18)You must use the experimental loader flag. Note that Node.js will display a warning about this being an experimental feature:
node --loader ts-node/esm ./src/index.ts
### Update your tsconfig.jsonTo make `ts-node` work with ESM, your `tsconfig.json` needs a specific `ts-node` configuration block:
{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext" }, "ts-node": { "esm": true } }
## Solution 3: The Production Fix (Transpilation)Running `.ts` files directly is great for coding, but it's risky for production. The most robust approach is to compile your code to standard JavaScript before deploying.
### 1. Build the project```
npx tsc
2. Run the outputOnce compiled, run the resulting JavaScript from your build folder (usually dist or out):
node dist/index.js
Because the output is standard .js, Node's ESM loader handles it without any complaints. Note: In ESM mode, your TypeScript import statements must include the .js extension, even if the source file is .ts.
Quick VerificationNot sure if it's fixed? Create a file named check.ts and add this code:
const message: string = "Loader is working!";
console.log(message);
Run npx tsx check.ts. If you see the message without a stack trace, your environment is ready.
Key Takeaways
- Use .js in imports: ESM requires full file extensions. Always write
import { tool } from './tool.js';. - Try .mts: Using the
.mtsextension forces Node to treat a file as an ES Module regardless of yourpackage.jsonsettings. - Stay Updated: If you are on a version older than Node 18, consider upgrading to v20+ to access the more stable
--importflag.

