WebAssembly (wasm) is an emerging standard – the future is coming. The support in browsers is already growing – what we still need is lowering the entry barrier for us, developers. Luckily, that is soon to be changed. I want to present you with a quick overview of something, that quite struck me – compiling TypeScript to WebAssembly. How is that possible? How close is it to be usable in production? Let’s find out!
What is WebAssembly and why would you want to use it?
WebAssembly is a new, low-level language that runs with a near-native performance.
But hey, we like working with our high-level languages, don’t we? Sure we do! There are not that many people writing assembler code nowadays. You may not want to write it yourself, but having a browser understand it opens many new doors.
TypeScript is statically typed. It means that we define types for our variables and we need to stick to them later on – it defines boundaries of some kind. In a way, it is similar to other statically typed languages, like C# or Java. It differs greatly, though – the information about types is used only during the process of transpiling our code to plain old JavaScript. It means losing all the information about types in the end – we do not get any optimization from it. Libraries that I want to show you today introduce new types of variables and are a little bit similar to truly statically typed languages.
Running WebAssembly in your JavaScript code
Wasm is yet to be integrated with ES6 import, unfortunately. It is worth mentioning that Webpack was awarded $125,000 from MOSS Program (Mozilla’s program for supporting the Open Source and Free Software movement) to better handle WebAssembly, so we may expect a much better experience in the future.
If you don’t want to wait, just don’t! There is a way.
First, you need to create an ArrayBuffer (raw binary data buffer) with the WebAssembly module binary, then compile it using WebAssembly.instantiate().
Imagine using a string to create a brand new function (you can do that in JavaScript!)
1 |
new Function(string); |
But this time you are passing an array buffer of bytes: WebAssembly source code.
1 2 3 4 5 6 7 8 |
fetch('fibonacci.wasm').then(response => response.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes) ).then(results => { const fib = results.instance.exports.fib; console.log(fib(40)); // 102334155 }); |
The most crucial part of the result of WebAssembly.instantiate() is an instance: it contains all the exported WebAssembly functions that could be called from JavaScript. They are just JS wrappers representing wasm functions.
Let me walk you through some compilers that are currently in development and aspire to compile TypeScript (or a language based on it) to WebAssembly.
AssemblyScript
It compiles strictly typed TypeScript using Binaryen. It works as a subset of TS syntax – you can choose to compile to WebAssembly or JavaScript. It introduces new types, for example, integers (both signed and unsigned) and floats. They can be 8bit, 16bit, 32bit or 64bit sized.
1 2 3 4 5 6 7 8 9 10 11 |
export function fib(n: i32): i32 { let prev:i32 = -1; let result:i32 = 1; let sum:i32; for(let i:i32 = 0; i <= n; ++i) { sum = result + prev; prev = result; result = sum; } return result; } |
I use the iterative instead of the recursive version of the Fibonacci algorithm because the latter would cause the call stack to exceed when calculating bigger values. If you export a function in your wasm code, it will be accessible from the WebAssembly instance.
AssemblyScript still has quite a road to go. You can look their checklist up at their Status and Roadmap. But, along with that, it brings some limitations to your TypeScript code: the first thing that strikes the most is no any type: can’t blame them though. It would mean sacrificing the profit gained from real static typing and would result in slowing things down.
It isn’t even on npm yet, but you can install it directly from GitHub with
1 |
npm install AssemblyScript/assemblyscript |
You can try it out at webassembly.studio or Assembleash
TurboScript
At first glance, it does look similar to AssemblyScript, and it has a similar set of types to choose from. There is quite a difference, though: it is not an actual TypeScript, but it is just based on it. As a result, it does not have TypeScript in its dependencies. I looked through its package.json, and its only dependency seems to be Binaryen. It does not have the any type either: all of the TurboScript modules need to be strongly typed. There is no garbage collector either. You can look up all of the types it provides. Its contributors describe it as “still an experiment and not intended for real use yet”.
You can try it out at the playground
Speedy.js
Installing it in its current state is a lot more complicated since it might require actually building LLVM from source. To use it, you will also need to install their own, forked TypeScript, which has int type added. What is very interesting, it has quite a different approach to writing code that is going to be compiled to WebAssembly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
async function fib(value: int): Promise<int> { "use speedyjs"; return fibSync(value); } function fibSync(value: int): int { "use speedyjs"; if (value <= 2) { return 1; } return fibSync(value - 2) + fibSync(value - 1); } async function main() { console.log(await fib(40)); } |
As you can see in the example from their page, it uses syntax with a directive similar to “use strict”. Furthermore, if you would like to call a function that uses Speedy.js inside your code, you need to mark it as async. It is worth mentioning that the compiler will compile fib and fibSync functions to WebAssembly, while the main function will remain pure JavaScript.
You also can play with Speedy.js on Assembleash
Summary
None of the presented TypeScript to WebAssembly compilers that I’ve brought up are ready to be used in a way other but experimental, but it certainly is an interesting topic and I observe the progress of their development. Is it still TypeScript though, since it won’t handle the any type? It is a sure thing that writing code for compilers like that would differ a lot from just writing pure TypeScript.
Since WebAssembly support is now shipping in all major browsers, wasm is definitely a thing that an up-to-date web developer should be familiar with.