Home

Awesome

TypeScript Native Compiler

Powered by LLVM|MLIR

Donate

Build

Test Build (Windows) Test Build (Linux)

What's new

class Point {
    private x: number;
    #y: number;
}

const p = new Point();
p.x // access error
p.#y // error
class Point {
    x: number;
    y: number;
}

class Line {
    constructor(public start: Point, public end: Point) { }
}

const l = new Line({ x: 0, y: 1 }, { x: 1.0, y: 2.0 });
function isArray<T extends unknown[]>(value: T): value is T {
    return true;
}

function gen<T>(t: T)
{
    if (isArray(t))
    {
        return t.length.toString();
    }

    return "int";
}

const v1 = gen<i32>(23); // result: int
const v2 = gen<string[]>([]); // result: 0
class Test {
    // declare index (to assigning get/set methods to it)
    [index1: number]: string;
    
    get(index: number): string {
        return "index";
    }

    set(index: number, value: string) {
    }

    get val(): string {
        return "prop";
    }
}

interface ITest {
    [index1: number]: string;

    get val(): string;
}

const t = new Test();
print(t[10]);

const ti: ITest = t;
print(ti[10]);
print(ti.val);
const arr = [1, 2, 3, 4, 5];
for (const b of arr)
    print(b);
let obj = {
    p: 1.0,
    get value() { return this.p; },
    set value(v: number) { this.p = v; },
}
class C {
    static x: number;
    static {
        C.x = 1;
    }
}
tsc --di --opt_level=0 --emit=exe example.ts
let a: string | number = 5;
let b: string = a; // b is "5"
static class Array {
    public of(...arg) {
        return arg;
    }

    public from(arrayLike) {
        return [...arrayLike];
    }       
}
// byte, short, ushort, int, uint, long, ulong, char, i8, i16, i32, i64,
// u8, u16, u32, u64, s8, s16, s32, s64, f16, f32, f64, f128, half, float, double, index

const s1: s8 = -1;
const s2: u16 = 2;
const s3: i32 = 3;
const s4: f64 = 1.0;
let a = [1, 2, 3];
const view: Reference<TypeOf<1>> = ReferenceOf(a[1]);
const data = LoadReference(view);
const data1 = LoadReference(view[1]);
class Person {
    static accessor sname: string;
    accessor name = "no value";
    constructor(name: string) {
        this.name = name;
    }
}
function main()
{
    using file = new TempFile(".some_temp_file");
    print("done.");
}

class TempFile {
    #path: string;
    #handle: number;
    constructor(path: string) {
        this.#path = path;
        this.#handle = 1;
    }
    // other methods
    [Symbol.dispose]() {
        // Close the file and delete it.
        this.#handle = 0;
        print("dispose");
    }
}

Planning

Demo

(click here)

Demo

Try it

(click here)

Compiler Explorer

Chat Room

Want to chat with other members of the TypeScriptCompiler community?

Join the chat at https://gitter.im/ASDAlexander77/TypeScriptCompiler

Example

abstract class Department {
    constructor(public name: string) {}

    printName(): void {
        print("Department name: " + this.name);
    }

    abstract printMeeting(): void; // must be implemented in derived classes
}

class AccountingDepartment extends Department {
    constructor() {
        super("Accounting and Auditing"); // constructors in derived classes must call super()
    }

    printMeeting(): void {
        print("The Accounting Department meets each Monday at 10am.");
    }

    generateReports(): void {
        print("Generating accounting reports...");
    }
}

function main() {
    let department: Department; // ok to create a reference to an abstract type
    department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
    department.printName();
    department.printMeeting();
    //department.generateReports(); // error: department is not of type AccountingDepartment, cannot access generateReports
}

Run

tsc --emit=jit --opt --shared-libs=TypeScriptRuntime.dll example.ts

Result

Department name: Accounting and Auditing
The Accounting Department meets each Monday at 10am.

Run as JIT

tsc --emit=jit --opt --shared-libs=TypeScriptRuntime.dll hello.ts
tsc --emit=jit --nogc hello.ts

File hello.ts

function main() {
    print("Hello World!");
}

Result

Hello World!

Compile as Binary Executable

On Windows

File tsc-compile.bat

set FILENAME=%1
set GC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\gc\msbuild\x64\release\Release
set LLVM_LIB_PATH=C:\dev\TypeScriptCompiler\__build\llvm\msbuild\x64\release\Release\lib
set TSC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\lib
set TSCEXEPATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\bin
%TSCEXEPATH%\tsc.exe --opt --emit=exe %FILENAME%.ts

Compile

tsc-compile.bat hello

Run

hello.exe

Result

Hello World!

On Linux (Ubuntu 20.04 and 22.04)

File tsc-compile.sh

FILENAME=$1
export TSC_LIB_PATH=~/dev/TypeScriptCompiler/__build/tsc/linux-ninja-gcc-release/lib
export LLVM_LIB_PATH=~/dev/TypeScriptCompiler/3rdParty/llvm/release/lib
export GC_LIB_PATH=~/dev/TypeScriptCompiler/3rdParty/gc/release
TSCEXEPATH=~/dev/TypeScriptCompiler/__build/tsc/linux-ninja-gcc-release/bin
$TSCEXEPATH/tsc --emit=exe $FILENAME.ts --relocation-model=pic

Compile

sh -f tsc-compile.sh hello

Run

./hello

Result

Hello World!

Compiling as WASM

On Windows

File tsc-compile-wasm.bat

set FILENAME=%1
set GC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\gc\msbuild\x64\release\Release
set LLVM_LIB_PATH=C:\dev\TypeScriptCompiler\__build\llvm\msbuild\x64\release\Release\lib
set TSC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\lib
C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\bin\tsc.exe --emit=exe --nogc -mtriple=wasm32-unknown-unknown %FILENAME%.ts

Compile

tsc-compile-wasm.bat hello

Run run.html

<!DOCTYPE html>
<html>

<head></head>

<body>
    <script type="module">
        let buffer;
        let buffer32;
        let buffer64;
        let bufferF64;
        let heap;

        let heap_base, heap_end, stack_low, stack_high;

        const allocated = [];

        const allocatedSize = (addr) => {
            return allocated["" + addr];
        };

        const setAllocatedSize = (addr, newSize) => {
            allocated["" + addr] = newSize;
        };

        const expand = (addr, newSize) => {

            const aligned_newSize = newSize + (4 - (newSize % 4))

            const end = addr + allocatedSize(addr);
            const newEnd = addr + aligned_newSize;

            for (const allocatedAddr in allocated) {
                const beginAllocatedAddr = parseInt(allocatedAddr);
                const endAllocatedAddr = beginAllocatedAddr + allocated[allocatedAddr];
                if (beginAllocatedAddr != addr && addr < endAllocatedAddr && newEnd > beginAllocatedAddr) {
                    return false;
                }
            }

            setAllocatedSize(addr, aligned_newSize);
            if (addr + aligned_newSize > heap) heap = addr + aligned_newSize;
            return true;
        };

        const endOf = (addr) => { while (buffer[addr] != 0) { addr++; if (addr > heap_end) throw "out of memory boundary"; }; return addr; };
        const strOf = (addr) => String.fromCharCode(...buffer.slice(addr, endOf(addr)));
        const copyStr = (dst, src) => { while (buffer[src] != 0) buffer[dst++] = buffer[src++]; buffer[dst] = 0; return dst; };
        const ncopy = (dst, src, count) => { while (count-- > 0) buffer[dst++] = buffer[src++]; return dst; };
        const append = (dst, src) => copyStr(endOf(dst), src);
        const cmp = (addrL, addrR) => { while (buffer[addrL] != 0) { if (buffer[addrL] != buffer[addrR]) break; addrL++; addrR++; } return buffer[addrL] - buffer[addrR]; };
        const prn = (str, addr) => { for (let i = 0; i < str.length; i++) buffer[addr++] = str.charCodeAt(i); buffer[addr] = 0; return addr; };
        const clear = (addr, size, val) => { for (let i = 0; i < size; i++) buffer[addr++] = val; };
        const aligned_alloc = (size) => { 
            const aligned_size = size + (4 - (size % 4)); 
            if ((heap + aligned_size) > heap_end) throw "out of memory"; 
            setAllocatedSize(heap, aligned_size); 
            const heapCurrent = heap; 
            heap += aligned_size; 
            return heapCurrent; 
        };
        const free = (addr) => delete allocated["" + addr];
        const realloc = (addr, size) => {
            if (!expand(addr, size)) {
                const newAddr = aligned_alloc(size);
                ncopy(newAddr, addr, allocatedSize(addr));
                free(addr);
                return newAddr;
            }

            return addr;
        }

        const envObj = {
            memory: new WebAssembly.Memory({ initial: 256 }),
            table: new WebAssembly.Table({
                initial: 0,
                element: 'anyfunc',
            }),
            fmod: (arg1, arg2) => arg1 % arg2,
            sqrt: (arg1) => Math.sqrt(arg1),
            floor: (arg1) => Math.floor(arg1),
            pow: (arg1, arg2) => Math.pow(arg1, arg2),
            fabs: (arg1) => Math.abs(arg1),
            _assert: (msg, file, line) => console.assert(false, strOf(msg), "| file:", strOf(file), "| line:", line, " DBG:", path),
            puts: (arg) => output += strOf(arg) + '\n',
            strcpy: copyStr,
            strcat: append,
            strcmp: cmp,
            strlen: (addr) => endOf(addr) - addr,
            malloc: aligned_alloc,
            realloc: realloc,
            free: free,
            memset: (addr, size, val) => clear(addr, size, val),
            atoi: (addr, rdx) => parseInt(strOf(addr), rdx),
            atof: (addr) => parseFloat(strOf(addr)),
            sprintf_s: (addr, sizeOfBuffer, format, ...args) => {
                const formatStr = strOf(format);
                switch (formatStr) {
                    case "%d": prn(buffer32[args[0] >> 2].toString(), addr); break;
                    case "%g": prn(bufferF64[args[0] >> 3].toString(), addr); break;
                    case "%llu": prn(buffer64[args[0] >> 3].toString(), addr); break;
                    default: throw "not implemented"; 
                }

                return 0;
            },
        }

        const config = {
            env: envObj,
        };

        WebAssembly.instantiateStreaming(fetch("./hello.wasm"), config)
            .then(results => {
                const { main, __wasm_call_ctors, __heap_base, __heap_end, __stack_low, __stack_high } = results.instance.exports;
                buffer = new Uint8Array(results.instance.exports.memory.buffer);
                buffer32 = new Uint32Array(results.instance.exports.memory.buffer);
                buffer64 = new BigUint64Array(results.instance.exports.memory.buffer);
                bufferF64 = new Float64Array(results.instance.exports.memory.buffer);
                heap = heap_base = __heap_base, heap_end = __heap_end, stack_low = __stack_low, stack_high = __stack_high;
                try
                {
                    if (__wasm_call_ctors) __wasm_call_ctors();
                    main();
                }
                catch (e)
                {
                    console.error(e);
                }
            });
    </script>
</body>

</html>

Build

On Windows

Requirements:

First, precompile dependencies

cd TypeScriptCompiler
prepare_3rdParty.bat 

To build TSC binaries:

cd TypeScriptCompiler\tsc
config_tsc_release.bat
build_tsc_release.bat

On Linux (Ubuntu 20.04 and 22.04)

Requirements:

First, precompile dependencies

chmod +x *.sh
cd ~/TypeScriptCompiler
./prepare_3rdParty.sh

To build TSC binaries:

cd ~/TypeScriptCompiler/tsc
chmod +x *.sh
./config_tsc_release.sh
./build_tsc_release.sh