/commands/nodejs/run-typescript-file
node app.ts: run a TypeScript file directly with Node.js
- tool
- Node.js
- versions
- Node.js 22.18+, Node.js 23.6+
- os
- any
node app.ts runs a TypeScript file directly on Node.js 22.18 or later and 23.6 or later: Node.js strips the type annotations and executes the remaining JavaScript, with no build step, no ts-node and no tsx. It does not type-check, and it supports only TypeScript syntax that can be erased without generating code. Earlier 22.x releases (from 22.6) need the --experimental-strip-types flag.
Run a file with imports
cat > util.ts <<'EOF'
export interface User { name: string }
export const greet = (u: User): string => `hello ${u.name}`;
EOF
cat > app.ts <<'EOF'
import { greet, type User } from './util.ts';
const user: User = { name: 'Ada' };
console.log(greet(user));
EOF
node app.tsOutput
hello Ada
Two rules make this work. Relative imports keep the real .ts extension (./util.ts, not ./util), because Node.js resolves files exactly as written. Type-only imports use import type or the inline type keyword, so the import can be dropped completely when the types are removed. The TypeScript options verbatimModuleSyntax, allowImportingTsExtensions and erasableSyntaxOnly make your editor enforce the same rules.
Types are removed, not checked
cat > bad.ts <<'EOF'
const port: number = "8080";
console.log(port, typeof port);
EOF
node bad.tsOutput
8080 string
The annotation disappears at run time, so the value keeps the type it really has. Run npx tsc --noEmit to see the type error (TS2322).
Syntax that needs a transform
cat > color.ts <<'EOF'
enum Color { Red, Green }
console.log(Color.Green);
EOF
node color.ts 2>&1 | grep -E "^SyntaxError"
echo "--- with --experimental-transform-types"
node --experimental-transform-types color.ts 2>&1 | grep -v -E "ExperimentalWarning|trace-warnings"Output
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode --- with --experimental-transform-types 1
| TypeScript feature | node file.ts | Workaround |
|---|---|---|
| Type annotations, interfaces, type aliases, generics | Runs | None needed |
| import type and inline type imports | Runs | None needed |
| enum, namespace with values, parameter properties | ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX | --experimental-transform-types, or plain objects and explicit fields |
| Legacy decorators (experimentalDecorators) | Not supported | Compile with tsc or use a loader such as tsx |
| .tsx files | Not supported | Compile with tsc, esbuild or a bundler |
| Path aliases from tsconfig.json | Not resolved | Use package.json "imports" (#alias) or relative paths |
| .ts files inside node_modules | Refused | Publish compiled JavaScript in packages |
Module format
A .ts file follows the same rules as a .js file: it is an ES module when the nearest package.json has "type": "module", or when Node.js detects import/export syntax; .mts is always ESM and .cts always CommonJS. Mixing require and import in one file gives the same errors as in JavaScript, such as Cannot use import statement outside a module.
How to confirm it works
Run node --version: 22.18.0 or later (or 23.6.0 or later) strips types by default. On 22.6 to 22.17 add --experimental-strip-types; on 20.x and older the .ts extension is unknown and the file fails with ERR_UNKNOWN_FILE_EXTENSION. node --no-experimental-strip-types app.ts turns the feature off, which shows whether type stripping is what runs your file.
FAQ
Does node app.ts check my types?
No. Node.js only removes the type annotations and runs what is left, so a type error still runs. Keep tsc --noEmit in your test or CI step to catch type errors.
Do I still need ts-node or tsx?
Not for plain TypeScript that uses only erasable syntax on Node.js 22.18 or later. You still need a loader such as tsx, or a build with tsc, for enums, namespaces, parameter properties, path aliases from tsconfig.json or .tsx files.
Why does Node.js ignore my tsconfig.json?
Type stripping does not read tsconfig.json at all. Options such as paths, target or experimentalDecorators have no effect; only the file itself is transformed.