An ahead-of-time (AOT) compiler that turns a subset of TypeScript into a standalone native executable — the way C++ or Rust do. There's no Node, V8, or JIT at runtime: the output is a self-contained binary.
.ts source ──▶ native executable
This is a learning-oriented compiler. It favors a clear, working end-to-end pipeline over feature breadth. See the language scope below for exactly what compiles.
- macOS on Apple Silicon (arm64) — the only configuration that's tested.
- Node.js ≥ 22
clang++on yourPATH— install the Xcode Command Line Tools if you don't have it:The compiler emits C++;xcode-select --install
clang++compiles + links it into the final binary.
Run it without installing anything via npx (use the package name, tsn-compiler):
npx tsn-compiler <file.ts> [-o <output>] [--emit-cpp]Or install it globally — the command it installs is tsn-compiler:
npm install -g tsn-compiler
tsn-compiler <file.ts>| Option | Description |
|---|---|
-o, --output <path> |
Output executable path. Defaults to the source file's basename. |
--emit-cpp |
Also write the generated C++ to <output>.cpp for inspection. |
-h, --help |
Show help. |
Create hello.ts:
console.log(20 + 22);Compile and run it:
npx tsn-compiler hello.ts -o hello
./hello
# 42Want to see the generated C++? Add --emit-cpp:
npx tsn-compiler hello.ts -o hello --emit-cpp
cat hello.cppThe goal is a small but complete pipeline. These features compile and run today:
- Types:
number,boolean,string, number/string arrays (T[]), and object literals with typed fields ({ x: number; y: number }). console.log(...)for numbers, booleans, and strings.- Arithmetic:
+ - * / %(numberis IEEE double, so5 / 2 === 2.5), unary-/+ - Comparisons & logic:
< <= > >= === !==(numbers and strings),&& || ! - Strings: literals, concatenation (
"a" + b; numbers coerce, e.g."n=" + 5), lexicographic comparison,s.length, indexings[i], and methodssubstring/slice/indexOf/charAt/charCodeAt/toUpperCase/toLowerCase - Variables:
let/const(the type is inferred when you omit the annotation;varis not supported), assignment (x = e,a[i] = e,obj.f = e,+=,i++) - Control flow:
if/else,while,for - Functions: top-level, typed params + return type,
return, and calls (recursion works) - Arrays: literals (incl. empty
[]), indexing (xs[i], computed indices),.length,.push(v) - Objects: literals and field access (
p.x)
function square(n: number): number {
return n * n;
}
function sumOfSquares(a: number, b: number): number {
return square(a) + square(b);
}
console.log(sumOfSquares(3, 4)); // 25
let xs: number[] = [10, 20, 30];
console.log(xs[0]); // 10
console.log(xs.length); // 3
let p: { x: number; y: number } = { x: 3, y: 4 };
console.log(p.x + p.y); // 7
let name = "Ada Lovelace";
console.log(name.length); // 12
console.log(name.toUpperCase()); // ADA LOVELACE
console.log(name.slice(0, 3)); // Ada
console.log("apple" < "banana"); // 1 (lexicographic; booleans print as 1/0)
console.logcurrently takes exactly one argument. Alet/constwithout a type annotation infers its type from the initializer — an integer literal likeconst a = 12compiles toint a = 12, a decimal todouble, and so on.
numberis an IEEEdouble(printed JS-style, shortest round-trip) — e.g.20 / 6prints3.3333333333333335. Functions and object fields still accept scalars only (number/boolean/string) — arrays/objects can't be passed or returned yet.varis not supported (and never will be) — useletorconst.- Out of scope for now:
null/undefined, classes, closures, exceptions,async, modules, generics, union/anytypes, and garbage collection. - Target is macOS arm64 only. Other platforms aren't supported yet.
The compiler runs four stages (see src/):
- Parse — the official
typescriptpackage parses your source into a TypeScript AST. - Lower — that AST is lowered into a small, typed internal IR (
src/ir/nodes.ts). - Codegen — the IR is emitted as C++ source (
src/codegen/emit.ts). - Build —
clang++compiles + links the.cppinto a native executable (src/backend/clang.ts).
git clone https://github.com/fardad-dev/typescript-native.git
cd typescript-native
npm install # also builds dist/ via the prepare script
npm run build # compile the compiler (tsc -> dist/)
npm test # compile each tests/cases/*.ts, run it, diff against *.expected
# run the local build directly without a global install
node dist/index.js examples/test1.ts -o out --emit-cpp && ./outEach language feature has a tests/cases/<name>.ts input paired with a <name>.expected
stdout file; tests/e2e.test.ts compiles, runs, and diffs them.
MIT — see LICENSE.