tsrun - TypeScript Interpreter in Rust
Description: A minimal TypeScript runtime in Rust for embedding. Use TypeScript for config files with IDE autocompletion, type checking, and error highlighting. No Node.js required.

# 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.

Try Playground Get Started

## 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

### CLI

bash

    cargo install tsrun

### Rust Library

toml

    [dependencies]
    tsrun = "0.1"

### C/C++ Embedding

bash

    cargo build --release --features c-api

## Use Cases

TypeScript configurations embedded in your application

Generate type-safe Kubernetes manifests with IDE autocompletion for deployment configs.

rust

    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.

rust

    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.

rust

    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.

rust

    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.

rust

    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