API reference

Type system

32/32Coverage14/32Strict (zero caveats)

TypeScript's type surface reinterpreted for whole-program AOT compilation, so the divergences are about what a static native target can and can't model. Advanced type-level machinery — mapped/utility/conditional types, generics, intersections, typeof queries — is evaluated at compile time via monomorphization, then erased, and assertions (`as T`, `as const`, `satisfies`) and modifiers (`readonly`) are likewise erased rather than enforcing. The primitives are JS-faithful (`number` is an IEEE-754 double; `bigint` is arbitrary-precision), with the extended integer/float JSDoc types as the escape hatch for real fixed-width integer semantics.

Exact Works, with caveats Not supported

number → double

Works, with caveats
let n: number

A JS-faithful IEEE-754 double: 0.1 + 0.2 is 0.30000000000000004, 10 / 3 is 3.3333333333333335, 1 / 0 is Infinity. The explicit JSDoc integer types are the opt-in escape hatch when real integer semantics matter.

console.log(0.1 + 0.2)   // 0.30000000000000004
console.log(10 / 3)      // 3.3333333333333335
console.log(2 ** -1)     // 0.5
⚠ Differences from standard
  • A plain number past 2^53 loses precision exactly as JS does (9007199254740993 is …992) — inherent to the double model, not a defect (use int64/uint64 for exact 64-bit integers)
  • Mixing an explicit integer type with a bare literal promotes to double (int32 / 2 is 3.5); use two integer-typed operands for integer division

string → ptr

Exact
let s: string

The string type, lowered to a pointer at the IR level.

boolean → i1

Exact
let b: boolean

The boolean type, lowered to a single-bit i1.

void

Exact
function f(): void

The absence of a return value on a function.

null / undefined

Exact
let x: T | null

The null and undefined values, represented as a sentinel null pointer.

JSDoc extended integers

Exact
/** @type {int32} */ let n = 0

A JSDoc @type override that gives a binding real fixed-width integer semantics: int8..int64 and uint8..uint64, with integer division, exact values past 2^53, and bit-width storage.

JSDoc extended floats

Exact
/** @type {float32} */ let x = 0

A JSDoc @type override selecting float32 or float64. Float-to-string is JS-faithful: the shortest decimal that round-trips, per ECMAScript Number::toString, so 1.1 + 2.2 prints 3.3000000000000003 and integer-valued floats drop the point. A float32 prints against its widened double.

any

Works, with caveats
let x: any

A runtime-tagged value supporting declare/assign/reassign/print/typeof/=== plus bare any params and returns on every function shape. Objects and arrays box by reference — === is reference identity, typeof is "object".

let x: any = 5
x = "hello"
x = true
console.log(typeof x)   // boolean
⚠ Differences from standard
  • Arithmetic on an any is a clean compile error
  • Nested positions any[]/{ x: any } are clean compile errors
  • A boxed array keeps only its data pointer — length/elements aren't recoverable, so it stringifies to the [object Array] tag, not its contents

unknown

Works, with caveats
let x: unknown

The type-safe counterpart to any, with the same Staged V1 scope: declare/assign/reassign/print/typeof/=== plus bare params and returns.

⚠ Differences from standard
  • Same as any (see above)

never

Exact
function f(): never

The bottom type. A (): never function that always throws works correctly.

symbol

Works, with caveats
const s = Symbol("desc")

Opaque unique values — Symbol()/Symbol("desc") (no new, Symbol is not a constructor), ===/!==, typeof, .description, .toString(), and the Symbol.for/Symbol.keyFor registry (keyFor gives undefined for a symbol not in it). Uniqueness comes from the value's own heap-pointer identity, so two Symbol() calls with the same description are never equal.

const a = Symbol("id")
const b = Symbol("id")
console.log(a === b)                  // false
console.log(Symbol.keyFor(Symbol.for("app")))  // app
⚠ Differences from standard
  • Of the well-known symbols the runtime honors Symbol.iterator, Symbol.asyncIterator and Symbol.toPrimitive (own properties, class and object-literal members); toStringTag, hasInstance, species and the rest are values only.

bigint

Works, with caveats
const n = 10n

Arbitrary-precision integers over a backend-agnostic ABI, selectable via -bigint=libtommath|gmp (default libtommath, public domain). Literals, the full arithmetic/bitwise operator set, same-type and cross-type-with-integer comparisons, typeof, ++/--, .toString([radix]), BigInt.asIntN/asUintN, Number(bigint), params/returns/fields/arrays, and BigInt(int|string) all work. Division/modulo by zero throws a catchable Error.

const a = 10n
console.log(a + 20n)              // 30n
console.log(BigInt.asIntN(8, 255n))  // -1n
console.log(Number(a))            // 10
⚠ Differences from standard
  • No .toLocaleString() (an Intl-shaped gap)

T | null (nullable)

Exact
let x: number | null

A single-branch T | null uses a presence-flagged nullable-scalar slot ({ i1, T }), so a genuine null is distinguishable from a legitimately-present 0 everywhere — ??, === null, printing, JSON — across locals, params, returns, fields, and Maps. A multi-branch A | B | null is handled by the union box instead.

let present: number | null = 0
let absent: number | null = null
console.log(present ?? 42)      // 0
console.log(absent ?? 42)      // 42

Object types (interfaces / inline {})

Exact
interface Point { x: number; y: number }

Interfaces, inline {} shapes, and type aliases. A literal's fields are coerced against the declared type wherever one is known — variable annotation, function parameter/return/default, array element type, or nested field — not just the literal's own self-inferred type.

interface Point {
  x: number;
  y: number;
}
type Rect = { width: number; height: number }

Array types T[]

Exact
let xs: number[]

Homogeneous arrays, stored as a { ptr, i64 } aggregate.

Promise<T>

Exact
async function f(): Promise<number>

The Promise<T> type for async values.

Function types (a: T) => R

Exact
let f: (a: number) => number

Function types, represented as a closure struct { funcPtr, envPtr }.

Map<K,V>

Exact
let m: Map<string, number>

The Map<K,V> collection, with separate helpers per key/value combination (<string,number>, <string,string>, etc.).

Set<T>

Exact
let s: Set<number>

The Set<T> collection.

Union types beyond T | null

Works, with caveats
let x: string | number

The same runtime box as any/unknown with a checked member set on top, statically restricted to the declared members at every assignment/call/return boundary. A narrowed local unboxes to the concrete member; an object member to its typed pointer. Usable for a variable, parameter, return value, object field and array element ((number | string)[], and (number | undefined)[] whose absent elements are real undefineds); every write is checked against the member set.

let x: string | number = "hello"
console.log(typeof x)    // string
x = 42
console.log(typeof x)    // number
⚠ Differences from standard
  • Scalar members (number/string/boolean + null/undefined), object members (classes, plain object types, one headerless host object such as URL, or a discriminated union), and a ReadableStream member; two array members of one kind, number-literal tags, and a non-first-position tag aren't supported
  • Flow narrowing is for a union **local** via typeof/truthiness/==null/instanceof/in/a tag (if/else branches + early return); a field/element, switch (typeof x), and as casts aren't narrowed; before narrowing an object union reads only a member all its object members declare

Intersection types

Works, with caveats
let x: HasName & HasAge

An object-type intersection collapses at resolveType into one merged ObjectType — member fields concatenated, compatible duplicates deduped — with no runtime tag, unlike a union. & binds tighter than |. Works as a named interface, inline {}, or type alias member, and in variable/param/return/field/array-element positions.

interface HasName { name: string }
interface HasAge { age: number }
const p: HasName & HasAge = { name: "Kyriakos", age: 34 }
⚠ Differences from standard
  • Object-type members only — a non-object member (scalar/function/array/tuple/union, all never in TS), or a generic-with-type-arguments member, is a clean compile error
  • A field declared with conflicting **non-object** types across members is rejected under -compat=strict (default); the -compat=js TS-faithful never-field is not yet implemented, so a conflict is currently rejected in both modes (same-named **object** fields are recursively intersected instead — { x: A } & { x: B } ⇒ x: A & B)
  • Inherits object types' missing-field leniency: a partial object literal for an A & B-typed target is not rejected (a pre-existing object-literal behavior, not intersection-specific)

Tuple types

Works, with caveats
const point: [number, number]

A fixed-arity, heterogeneous, positional value stored as a fixed-shape struct. Supports labelled elements ([min: number, max: number]), the tuple literal, constant-index read and assignment, .length (compile-time constant), destructuring (including nested and for-of), and array-shaped rendering. Map/Array/Object.entries() return real [K, V] tuples.

const point: [number, number] = [3, 4]
console.log(point[0])              // 3
const [name, value] = point
⚠ Differences from standard
  • No rest/optional elements
  • Constant index only — no non-constant index; compound assignment to an element (t[0] += x) is a clean rejection
  • No array methods (.map/.filter/…) on a tuple (.length works)
  • Not supported nested in any or a union

Mapped & utility types

Works, with caveats
type Clone = { [K in keyof T]: T[K] }

Partial/Required/Readonly/NonNullable/Pick/Omit/Record, the general mapped form { [K in keyof T]: V }, keyof, indexed access T[K], and string-literal types — all evaluated at compile time to concrete object types via the monomorphization seam, no runtime representation.

type Clone = { [K in keyof User]: User[K] }
⚠ Differences from standard
  • Partial/Required/Readonly are structural no-ops — the object model already zero-fills omitted fields and does no mutation checking; the visibly-effective utilities are Pick/Omit/Record
  • Mapped value body handles T[K] (homomorphic), a bare K, a concrete type (uniform), or T[K] wrapped one level in Array/Promise/Set; a T[K] nested deeper isn't substituted
  • No key remapping (as) or modifier-removal (-?/-readonly); ?/readonly are accepted but near-no-ops
  • String-literal types resolve to string — the literal value is not narrowed/enforced
  • An unrecognized Name<Args> (or a malformed Pick/Omit) is not yet a clean rejection

Conditional types (T extends U ? X : Y)

Works, with caveats
type ElementOf<T> = T extends Array<infer E> ? E : T

Evaluated at compile time off the monomorphized (concrete) check type — generic type aliases, infer capture, and a structural assignability predicate. Nested conditionals are right-associative.

type ElementOf<T> = T extends Array<infer E> ? E : T
const one: ElementOf<number[]> = 7
const raw: ElementOf<string> = "hi"
⚠ Differences from standard
  • infer supported for Array<infer E>/Promise<infer V> and a bare infer R; function-signature inference ((...) => infer R) and deeper infer nesting are deferred
  • assignable is structural width subtyping (objects), element-wise (arrays), IR-equality with numeric leniency (scalars) — not full variance

Generics on user functions/interfaces/classes

Works, with caveats
function identity<T>(x: T): T

V1 (default) is monomorphization — one specialized implementation per distinct combination of concrete types actually used. Covers function identity<T>, interface Box<T>, class Box<T>, and multiple type parameters <K, V>. <T extends X> constraints are parsed and enforced (structural width subtyping). V2 /** @erased */ compiles a generic function's body once, with no name mangling.

function identity<T>(x: T): T {
  return x;
}
console.log(identity(42));       // 42
console.log(identity("hello"));  // hello
⚠ Differences from standard
  • A generic function needs an inferable T/T[]-typed parameter for each of its type parameters
  • Map/Set/Promise/closure/dynamic type arguments are a clean rejection (scalars, arrays, and object/interface/class types are supported)
  • V2 @erased covers functions only (not interfaces/classes) and bare T positions only — T[] or T nested in an object field is a clean compile error
  • Arithmetic on an erased T hits the same "operator on any/unknown" rejection as plain any

Index signatures ({ [k: string]: T })

Works, with caveats
interface Scores { [player: string]: number }

A string index signature types a dictionary of arbitrary string keys, backed by a map.

interface Scores {
  [player: string]: number;
}
const scores: Scores = { alice: 10, bob: 7 };
scores["carol"] = 12;
⚠ Differences from standard
  • String or number key; the dictionary is the object itself, so a static object or an any value converted to it is the same object and writes show on both — d[key] read/write, in, delete, an object-literal initializer, and Object.keys/values/entries + for…of all work; a missing key reads undefined
  • Named properties beside the signature ({ id: number; [k: string]: number }) keep their declared types
  • JSON.stringify of an index-signature dict is compact output only (the space argument is not applied)
  • The class-body form (class C { [n: number]: T }) is dropped

typeof type queries (type T = typeof x)

Exact
type Config = typeof config

Derives a type from an existing value without restating its shape, usable as a type alias, an inline annotation, or a parameter/return type.

const config = { host: "localhost", port: 8080 };
type Config = typeof config;
const staging: Config = { host: "staging", port: 9090 };
⚠ Differences from standard
  • Resolves the referenced value's type in the query's own scope, as tsc does (a const gives its literal type; an unknown name is TS2304), walking a typeof a.b member path; typeof C of a class is the class itself, usable as a value

Type assertions (x as T, as const, satisfies T)

Works, with caveats
const x = value as T

Parsed as a left-associative postfix on any expression; as const and satisfies T are likewise identity.

⚠ Differences from standard
  • **Erased, not enforcing** — the assertion is accepted and dropped: the value is unchanged and keeps its *own* inferred type, so as T does not re-type the expression (a sound cross-representation reinterpret isn't modeled). This matches TS's runtime erasure but not its static narrowing/widening; a cast the code *relies on* for typing won't take effect. One carve-out: as T directly on JSON.parse(...)/res.json() supplies the projection target under -compat=strict, exactly as a declaration annotation would.

Template literal types ( a-${T} )

Works, with caveats
type Id = `user-${number}`

Template literal types parse in a type position (no-substitution, multi-substitution, and []-suffixed forms) and resolve to `string` — the literal pattern isn't narrowed or enforced, the same simplification string-literal types use.

⚠ Differences from standard
  • Erased to string — the literal pattern is not narrowed/enforced.

readonly T[] array-type modifier

Works, with caveats
let xs: readonly number[]

The readonly array-type modifier parses in variable/parameter/return/field positions.

⚠ Differences from standard
  • **Erased, not enforcing** — readonly number[] and readonly [T, U] parse (in variable/parameter/return/field positions), but the modifier is dropped and mutation is not prevented, the same stance as Readonly<T> and the mapped-type readonly modifier. The ReadonlyArray<T> alias form is not covered

Ambient declarations (declare const/function/class/module/namespace)

Works, with caveats
declare const BUILD_ID: string

declare ambient declarations describe types the environment provides; carrying no implementation, they are parsed and erased.

declare const BUILD_ID: string;
declare function nativeHook(x: number): number;
declare module "vendor-lib" {
  export function doThing(): void;
}
⚠ Differences from standard
  • Parsed and erased — the no-body/no-init forms and the brace-bodied global/module/namespace/class blocks all compile away. Ambient declarations describe externally-provided values; under whole-program AOT they have no target to bind to, so a runtime *use* of a declared binding this compiler doesn't itself provide is an ordinary undefined variable error

TypeScript type errors

Works, with caveats
let n: number = "x" // TS2322

The default strict lane rejects a program TypeScript rejects for these type errors, reporting tsc's code, message and position. -compat=js compiles the same program as JavaScript.

let n: number = "x"
// klainmain: type error: main.ts: 1:1: type 'string' is not assignable to type 'number'
//   hint: TypeScript rejects this program; -compat=js compiles it as JavaScript
⚠ Differences from standard
  • Reported so far: assignability, arguments and argument counts, arithmetic operands, comparisons with no overlap, missing and excess properties, calls no overload matches, possibly-null, possibly-undefined and unknown operands, and stores to constants, functions, classes and enums; a missing name and a read of a partly assigned variable are not
  • Calls to generic functions and to builtins are not checked yet
  • A function type in a message names its parameters p0, p1, … instead of their declared names