Write TypeScript.
Compile to a native binary.

Lumen type‑checks familiar TypeScript syntax and compiles it straight to a small, dependency‑free executable. No VM, no interpreter, no Node.

curl -fsSL https://lumen-lang.org/install.sh | sh
// Call libm directly -- no bindings, no runtime.
// @link m
declare function sqrt(x: number): number;

function hypot(a: number, b: number): number {
  using _ = defer(() => console.log("computed"));   // runs at scope exit
  return sqrt(a * a + b * b);
}

console.log(hypot(3.0, 4.0));   // computed → 5

Hello, Lumen

Familiar syntax, fixed static types, no runtime, just a native executable.

interface Point {
  x: int;
  y: int;
}

function distanceSq(a: Point, b: Point): int {
  let dx = a.x - b.x;
  let dy = a.y - b.y;
  return dx * dx + dy * dy;
}

let origin: Point = { x: 0, y: 0 };
let p: Point = { x: 3, y: 4 };
console.log(`distance² = ${distanceSq(origin, p)}`);  // distance² = 25

What's in the language

A predictable static subset of TypeScript, no prototypes, no eval, no dynamic shapes.

Static types
Local inference and return-type inference for functions, arrows, and methods, int/i64/number/bool/string, float & hex/binary literals. Numeric promotion (int + number) and lossless int → i64 widening, just like TypeScript's single number.
Records, interfaces & unions
Closed object shapes, nested records, arrays of records, enums, and fixed‑shape tuples like [int, string]. Structural width subtyping: a wider record flows into a narrower one, so interface parameters accept any matching shape. Plus type aliases, discriminated and string‑literal unions, as assertions, as const, satisfies, and readonly arrays.
Control flow & operators
if/else if, while, do, C‑style for, for…of (arrays, strings, Set, Map), for…in, switch (exhaustive over literal unions and enums), ternary, and labeled break/continue. The full arithmetic, bitwise, and shift set, with every compound‑assignment form (+=, &&=, ??=, <<=, **=, and more).
Null safety & narrowing
T | null, optional ? fields, ??, optional chaining ?./?.[i]/?.()/?.length, and the non-null assertion x!. Flow narrowing across if/else, guard clauses (if (x == null) return), while, &&/||, and deep field paths (a.b.c != null) — plus discriminated-union complement narrowing.
Functions & parameters
Function types, arrow functions, and capturing closures. Default values, rest parameters, and spread, checked statically, no arguments object. Object shorthand { x } and computed keys, destructuring defaults in array ([a = 1]) and object ({ x = 1 }) patterns, and destructuring assignment ([a, b] = [b, a]).
Classes & inheritance
extends, super, public/private/protected, #private fields, static, readonly, and get/set. Fields infer their type from an initializer (count = 0); methods take default, optional, and rest parameters.
Generics
Generic functions, classes, and interfaces with Array<T>, monomorphized at compile time, no boxing. Type-parameter constraints (<T extends ...>) and defaults (<T = ...>).
Strings & regex
Single‑ and double‑quoted literals, template literals `hi ${name}` with string & numeric interpolation, and the methods you expect (split, slice, trim, includes, indexOf, replace, repeat, padStart, toUpperCase, and more). /pattern/ literals with .test(): anchored patterns compile to specialized native matchers at build time, ~3× faster than V8 on checks like semver and identifiers, and everything else falls back to a built-in engine, so every pattern works.
Collections & helpers
Map<K, V> and Set<T>, both iterable with for…of, and the array methods you expect (map, filter, reduce, forEach, find, some, every, includes, indexOf, join). Plus Date.now(), Object.keys/Object.freeze, Array.isArray/Array.from/Array.of, JSON.stringify/parse<T>, parseInt/parseFloat, and the full Math/Number surface.
Errors, defer & tests
try/catch/finally and throw, propagating across functions, methods, and constructors; readable .message, optional catch binding, and full stack traces on uncaught errors. Scope‑exit cleanup with defer (last‑in‑first‑out), and built‑in tests: test "…" { expect(…); } run with lumen test.
Async, threads & events
async functions return Promise<T> and await resolves them on a fast native event loop, with setTimeout/setInterval and their clear* counterparts. Worker.run(fn) spawns a real, detached OS thread — genuine CPU parallelism, with no per‑thread interpreter overhead to pay. EventEmitter<T> is statically typed, one payload type per instance, and ~3.5× faster than Node's on a tight emit loop.
Buffer, crypto & I/O
A real byte‑array type, distinct from string at compile time: Buffer.from/.alloc, utf8/hex/base64 encodings, .slice/.at/.equals. crypto.hmacSync and AES‑256‑GCM encryptSync/decryptSync both read and return it. Alongside net TCP sockets, an http client & concurrent server, process.stdin/stdout/stderr as streams, readline.question, and zlib gzip/deflate.
Modules & native output
Default, named, and import * as ns imports, and export … from re-exports, from a relative file or an https:// URL — a package is just a URL. declare function + // @link calls native C libraries, with scalars and strings marshalling across the boundary. It all compiles to a small, dependency‑free native binary.

See these as complete programs →

Errors that explain themselves

Compile errors say what's wrong and how to fix it — expected/got types, did‑you‑mean suggestions, caret underlines, and color. Uncaught runtime errors print a real stack trace with your file and line, even across imports.

$ lumen check main.ts
main.ts:4:1: error: 'add' expects 2 arguments, got 1
main.ts:6:1: error: `string` has no method 'toUperCase' — did you mean 'toUpperCase'?
main.ts:8:1: error: 'r' (`string | null`) may be null — check `!= null`
  before reading '.length', or use optional chaining `?.length`
main.ts:9:1: error: `if` condition must be `boolean`, got `i32` —
  truthiness is not supported; write `x != 0`

$ lumen run app.ts
util.ts:2:14: Uncaught Error: negative input
  2 |   if (n < 0) throw new Error("negative input")
    |              ^
    at risky (util.ts:2:14)
    at process (app.ts:7:3)
    at <main> (app.ts:12:1)

$ lumen test app.ts
ok adds
FAIL rounds — expected 4, found 3
    at app.ts:21
1 passed, 1 failed

Install

curl -fsSL https://lumen-lang.org/install.sh | sh

Windows: download the .zip from the releases page. Self-contained, no other toolchain required.

Then scaffold a project and run it:

lumen init my-app
cd my-app
lumen compile main.ts && ./main

lumen init writes a starter main.ts plus the ambient declarations and tsconfig.json, so the project is editor- and type-checker-clean from the first keystroke.

For a live edit loop, lumen watch rebuilds and re-runs the program whenever the entry file or any of its local imports changes:

lumen watch main.ts            # rebuild and re-run on change
lumen watch --no-run main.ts   # rebuild only