tsrun
TypeScript Interpreter in Rust
A minimal TypeScript runtime designed for embedding in applications. Perfect for configuration files where you want autocompletion, type checking, and error highlighting in your editor.
Features
TypeScript for configs with IDE autocompletion - embedded in your application
ES Modules
Full import/export support with step-based module loading. Load modules from filesystem, network, or virtual sources.
Async/Await
Promises, async functions, Promise.all/race/allSettled. Pause execution for host-provided async operations.
Classes
Full class support with inheritance, static blocks, private fields, getters and setters.
Generators
function*, yield, yield*, and for...of iteration. Create lazy sequences and iterators.
Built-ins
Array, String, Object, Map, Set, Date, RegExp, JSON, Math, Proxy, Reflect, Symbol and more.
TypeScript Native
Enums, interfaces, decorators, namespaces, generics, and type annotations. Types are stripped at runtime.
Minimal & Embeddable
Small footprint with Rust and C APIs. No Node.js dependency. Ideal for config files and scripting in host applications.
Installation
Multiple ways to use tsrun in your projects
Use Cases
TypeScript configurations embedded in your application
Generate type-safe Kubernetes manifests with IDE autocompletion for deployment configs.
use tsrun::{Interpreter, StepResult};
fn main() -> Result<(), tsrun::JsError> {
let mut interp = Interpreter::new();
interp.prepare(r#"
interface DeploymentConfig {
name: string;
image: string;
replicas: number;
port: number;
}
function deployment(config: DeploymentConfig) {
return {
apiVersion: "apps/v1",
kind: "Deployment",
metadata: { name: config.name },
spec: {
replicas: config.replicas,
selector: { matchLabels: { app: config.name } },
template: {
metadata: { labels: { app: config.name } },
spec: {
containers: [{
name: config.name,
image: config.image,
ports: [{ containerPort: config.port }]
}]
}
}
}
};
}
deployment({ name: "api", image: "myapp:v1.2.0", replicas: 3, port: 8080 })
"#, None)?;
loop {
match interp.step()? {
StepResult::Continue => continue,
StepResult::Complete(value) => {
let json = tsrun::js_value_to_json(value.value())?;
println!("{}", json);
break;
}
_ => break,
}
}
Ok(())
}
Define game items with enums and computed loot tables for drop rates and pricing.
use tsrun::{Interpreter, StepResult};
fn main() -> Result<(), tsrun::JsError> {
let mut interp = Interpreter::new();
interp.prepare(r#"
enum Rarity { Common, Rare, Epic, Legendary }
interface Item {
name: string;
rarity: Rarity;
basePrice: number;
effects?: string[];
}
function createLootTable(items: Item[]) {
return items.map(item => ({
...item,
dropWeight: item.rarity === Rarity.Legendary ? 1 :
item.rarity === Rarity.Epic ? 5 :
item.rarity === Rarity.Rare ? 15 : 50,
sellPrice: Math.floor(item.basePrice * (1 + item.rarity * 0.5))
}));
}
createLootTable([
{ name: "Iron Sword", rarity: Rarity.Common, basePrice: 100 },
{ name: "Dragon Scale", rarity: Rarity.Legendary, basePrice: 5000,
effects: ["Fire Resistance", "+50 Defense"] }
])
"#, None)?;
loop {
match interp.step()? {
StepResult::Continue => continue,
StepResult::Complete(value) => {
let json = tsrun::js_value_to_json(value.value())?;
println!("{}", json);
break;
}
_ => break,
}
}
Ok(())
}
Configure REST endpoints with typed middleware and rate limiting rules.
use tsrun::{Interpreter, StepResult};
fn main() -> Result<(), tsrun::JsError> {
let mut interp = Interpreter::new();
interp.prepare(r#"
interface Route {
method: "GET" | "POST" | "PUT" | "DELETE";
path: string;
handler: string;
middleware?: string[];
rateLimit?: { requests: number; window: string };
}
const routes: Route[] = [
{
method: "GET",
path: "/users/:id",
handler: "users::get",
middleware: ["auth", "cache"]
},
{
method: "POST",
path: "/users",
handler: "users::create",
middleware: ["auth", "validate"],
rateLimit: { requests: 10, window: "1m" }
},
{
method: "DELETE",
path: "/users/:id",
handler: "users::delete",
middleware: ["auth", "admin"]
}
];
routes
"#, None)?;
loop {
match interp.step()? {
StepResult::Continue => continue,
StepResult::Complete(value) => {
let json = tsrun::js_value_to_json(value.value())?;
println!("{}", json);
break;
}
_ => break,
}
}
Ok(())
}
Create plugin-based build configurations similar to webpack or vite.
use tsrun::{Interpreter, StepResult};
fn main() -> Result<(), tsrun::JsError> {
let mut interp = Interpreter::new();
interp.prepare(r#"
interface Plugin {
name: string;
options?: Record<string, any>;
}
interface BuildConfig {
entry: string;
output: { path: string; filename: string };
plugins: Plugin[];
minify: boolean;
}
const config: BuildConfig = {
entry: "./src/index.ts",
output: {
path: "./dist",
filename: "[name].[hash].js"
},
plugins: [
{ name: "typescript", options: { target: "ES2022" } },
{ name: "minify", options: { dropConsole: true } },
{ name: "bundle-analyzer" }
],
minify: true
};
config
"#, None)?;
loop {
match interp.step()? {
StepResult::Continue => continue,
StepResult::Complete(value) => {
let json = tsrun::js_value_to_json(value.value())?;
println!("{}", json);
break;
}
_ => break,
}
}
Ok(())
}
Define form validation schemas with discriminated unions for type-safe rules.
use tsrun::{Interpreter, StepResult};
fn main() -> Result<(), tsrun::JsError> {
let mut interp = Interpreter::new();
interp.prepare(r#"
type Rule =
| { type: "required" }
| { type: "minLength"; value: number }
| { type: "maxLength"; value: number }
| { type: "pattern"; regex: string; message: string }
| { type: "email" };
interface FieldSchema {
name: string;
label: string;
rules: Rule[];
}
const userSchema: FieldSchema[] = [
{
name: "email",
label: "Email Address",
rules: [
{ type: "required" },
{ type: "email" }
]
},
{
name: "password",
label: "Password",
rules: [
{ type: "required" },
{ type: "minLength", value: 8 },
{ type: "pattern", regex: "[A-Z]", message: "Must contain uppercase" }
]
}
];
userSchema
"#, None)?;
loop {
match interp.step()? {
StepResult::Continue => continue,
StepResult::Complete(value) => {
let json = tsrun::js_value_to_json(value.value())?;
println!("{}", json);
break;
}
_ => break,
}
}
Ok(())
}
Ready to try it?
Run TypeScript code directly in your browser with our interactive playground.
Open Playground