Skip to content

/commands/nodejs/run-typescript-file

node app.ts: run a TypeScript file directly with Node.js

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · v24.20.0updated
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

bash
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.ts

Output

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

bash
cat > bad.ts <<'EOF'
const port: number = "8080";
console.log(port, typeof port);
EOF
node bad.ts

Output

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

bash
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 featurenode file.tsWorkaround
Type annotations, interfaces, type aliases, genericsRunsNone needed
import type and inline type importsRunsNone needed
enum, namespace with values, parameter propertiesERR_UNSUPPORTED_TYPESCRIPT_SYNTAX--experimental-transform-types, or plain objects and explicit fields
Legacy decorators (experimentalDecorators)Not supportedCompile with tsc or use a loader such as tsx
.tsx filesNot supportedCompile with tsc, esbuild or a bundler
Path aliases from tsconfig.jsonNot resolvedUse package.json "imports" (#alias) or relative paths
.ts files inside node_modulesRefusedPublish 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.

Sources