Tsonic compiles a strict, deterministic subset of TypeScript into C#, then into native binaries or .NET libraries.
The V1 model is:
- one compiler-owned noLib core
- one active ambient surface per workspace
- explicit package-based CLR and module interop
- strict-AOT rejection for anything that cannot be lowered deterministically
- TypeScript authoring with explicit numeric/value semantics when needed
- NativeAOT and regular .NET outputs from the same compiler
- Direct CLR interop through
@tsonic/dotnetand generated bindings - JS-surface authoring through
@tsonic/js - Node module support through
@tsonic/nodejs - First-party source-package consumption for Tsonic-authored npm packages
Tsonic separates the language prelude from the ambient runtime personality.
- compiler core: always-on noLib baseline (
Promise, iterators, utility types, array shape) clrsurface: default ambient CLR-first world@tsonic/jssurface: JS-style globals and receiver methods@tsonic/nodejs: normal package, not a surface
That means:
- CLR workspace:
"abc"exposes CLR-shaped ambient members- import CLR APIs explicitly from
@tsonic/dotnet/...
- JS workspace:
"abc".trim(),[1, 2, 3].map(...),console.log(...),JSON,Datework as ambient JS APIs
- Node usage:
- keep JS surface active
- add
@tsonic/nodejs - import
node:*modules normally
npm install -g tsonicRequirements:
- Node.js 22+
- .NET 10 SDK
- NativeAOT toolchain for full builds/tests:
- Linux:
clangorgccavailable onPATH - macOS: Xcode Command Line Tools
- Windows: Visual Studio C++ build tools
- Linux:
For compiler development, use a sibling checkout layout. The compiler repo is expected to live beside the runtime and first-party package repos:
~/repos/tsoniclang/
tsonic/
runtime/
core/
dotnet/
globals/
js/
nodejs/
aspnetcore/
efcore/
efcore-sqlite/
microsoft-extensions/
The ../runtime dependency is intentional for this repo: tests and package
preflight copy Tsonic.Runtime.dll from the sibling runtime build. Most source
and binding package resolution paths use siblings only when they are present
and proven by a package.json; otherwise they use installed npm packages. The
full compiler gate also includes source-package graph tests that intentionally
require the authored ../js and ../nodejs source-package repos, because
published binding packages do not contain the source-package manifests or
transitive TypeScript source files those tests are proving.
cd ~/repos/tsoniclang/runtime
dotnet build -c Release
cd ../tsonic
npm ci
npm run build
./test/scripts/run-all.sh./test/scripts/run-all.sh performs a NativeAOT preflight before fixture
execution. A missing platform linker is a machine prerequisite failure, not a
compiler failure; install the platform toolchain before treating the full gate
as green.
The full test runner stores shared NuGet packages in .tests/nuget/packages
and removes per-fixture .tsonic, generated, out, and dist artifacts as
each fixture completes. For a focused fixture debug run, set
TSONIC_E2E_KEEP_ARTIFACTS=1 to preserve those artifacts for inspection.
To clean artifacts left by an interrupted run, use
./test/scripts/clean-fixture-artifacts.sh.
npm run build is a non-mutating compiler build. Use npm run format or
npm run format:check explicitly for formatting.
mkdir hello-clr
cd hello-clr
tsonic init
tsonic runGenerated sample:
import { Console } from "@tsonic/dotnet/System.js";
export function main(): void {
Console.WriteLine("Hello from Tsonic!");
}mkdir hello-js
cd hello-js
tsonic init --surface @tsonic/js
tsonic runGenerated sample:
export function main(): void {
const message = " Hello from Tsonic JS surface! ".trim();
console.log(message);
}mkdir hello-node
cd hello-node
tsonic init --surface @tsonic/js
tsonic add npm @tsonic/nodejsThen author normal Node-style imports:
import * as path from "node:path";
import * as fs from "node:fs";
export function main(): void {
const file = path.join("src", "App.ts");
console.log(file, fs.existsSync(file));
}Run it:
tsonic runUse @tsonic/core/types.js for CLR-specific numeric/value intent:
import type { int, long, bool } from "@tsonic/core/types.js";Use @tsonic/core/lang.js for language intrinsics:
import {
defaultof,
nameof,
sizeof,
stackalloc,
out,
} from "@tsonic/core/lang.js";Import CLR APIs explicitly:
import { Console } from "@tsonic/dotnet/System.js";
import { Enumerable } from "@tsonic/dotnet/System.Linq.js";
export function main(): void {
const xs = [1, 2, 3];
const filtered = Enumerable.Where(xs, (x: number): boolean => x > 1);
Console.WriteLine(filtered.Count().ToString());
}For external CLR dependencies:
tsonic add nuget Microsoft.Extensions.Logging 10.0.0
tsonic add package ./libs/MyCompany.MyLib.dll
tsonic restoretsonic init creates npm-publish-ready source packages by default. Each
project gets a source manifest at:
packages/<project>/tsonic.package.json
Example:
{
"schemaVersion": 1,
"kind": "tsonic-source-package",
"surfaces": ["@tsonic/js"],
"source": {
"exports": {
".": "./src/App.ts",
"./index.js": "./src/App.ts"
}
}
}Installed source packages with that manifest are compiled transitively as part of the same Tsonic program.
tsonic generate
tsonic build
tsonic run
tsonic test
tsonic packSupported output shapes include:
- NativeAOT executable
- managed executable
- managed library
- NativeAOT shared/static library
- AST-only emitter pipeline
- canonical type identity keys for type comparison, overload matching, and runtime-union decisions
- source-package graphs compiled transitively with source-backed metadata retained through call, constructor, and narrowing paths
- runtime union carriers that preserve union arm identity instead of lowering
ambiguous values through
object - Promise constructor +
then/catch/finallylowering - static ESM import graphs only; dynamic
import()andimport.metaare not emitted language features - typed JSON parse/stringify lowering through compile-time serialization metadata
- broader object-literal support:
- accessors
- computed constant keys
- shorthand methods
- supported
arguments.length/arguments[index]cases
nameof(...)andsizeof<T>()- deterministic generic function values in supported monomorphic contexts
- User guide:
docs/README.md - Site:
https://tsonic.org/tsonic/ - Architecture:
docs/architecture/README.md
MIT