AssemblyScript vs. TypeScript: the Differences Developers Need to Know

AssemblyScript vs. TypeScript: the Differences Developers Need to Know

As codebases have grown in complexity, the dynamic typing nature of JavaScript has become arguably suboptimal. TypeScript was introduced by Microsoft as a superset of JavaScript back in 2012, with version 1.0.0 out in 2014. It introduced a more strict and structured way of writing with data types and features that were missing altogether in JavaScript.

While either TypeScript or JavaScript is generally sufficient to solve today’s web development problems, JavaScript is limiting in extreme use cases such as AR, VR, and high fps 3D games. In such scenarios, we need native-like performance on the web, making WebAssembly perfect for this kind of problem.

WebAssembly is a relatively new concept for modern web browsers. It gives us the ability to run code from low-level languages, say C/C++ or Rust, easily. WebAssembly is fast because it’s distributed in a binary format (ie, already compiled), unlike high-level languages for the web such as TypeScript and JavaScript. The goal of WebAssembly is not to create a paradigm shift from the existing JavaScript ecosystem but to run side by side with JavaScript, supporting backward compatibility.

As WebAssembly requires writing low-level languages, web developers who are usually proficient in TypeScript or JavaScriptneed to learn a new syntax altogether. This is where AssemblyScript shines. It follows the very similar footsteps of TypeScript, so developers can work with a more familiar syntax.

AssemblyScript’s homepage

Note: AssemblyScript is not a TypeScript-to-WebAssembly compiler. Neither data types are similar to that of TypeScript.

According to AssemblyScript’s home page, "Being a variant of TypeScript makes it easy to compile to WebAssembly without learning a new language." AssemblyScript uses the Binaryen compiler. Unlike V8’s JIT compiler, which is used to execute JavaScript, Binaryen allows AssemblyScript to be converted to machine code much faster without any performance overhead.

Behaviors to Watch Out For in AssemblyScript

Now, let’s walk through some of AssemblyScript’s unexpected behavior and features that might come as a complete surprise to TypeScript developers.

Numbers

Traditionally in JavaScript, there was just a single representation of the number type with limits on its range. This issue was addressed later in ECMAScript standards by introducing a new number type called BigInt. TypeScript version 3.2 released support for BigInt, but the number types in AssemblyScript are more restrictive and type-safe with various floating points and specific number types.

A few of them are:

  • i8 - 8 bit signed integer
  • i16 - 16 bit signed integer
  • i32 - 32 bit signed integer
  • i64 - 64 bit signed integer
  • u8 - 8 bit unsigned integer
  • u16 - 16 bit unsigned integer
  • u32 - 32 bit unsigned integer
  • u64 - 64 bit unsigned integer
  • f32 - 32 bit float
  • f64 - 64 bit float

In addition to these, you have two variable integer types as well:

  • isize - supports i32 and i64
  • usize - supports u32 and u64

These leniencies for isize and usize will be uplifted in the upcoming release.

Array Sorting

Sorting an array of numbers in AssemblyScript can be fundamentally very different to TypeScript. Consider this example in TypeScript:

[3, 1, 2].sort();
> [1, 2, 3]

[20, 10, 3].sort()
> [10, 20, 3]

While the first array sorting behaves as expected, the second one doesn't make any sense. The reason for this is that TypeScript coerces the number to string using the toString() method, then compares its UTF-16 values alphabetically.

This mechanism of coercing has been improved in AssemblyScript. Instead of sorting alphabetically, it sorts numerically, and the results are as expected with no such weird quirks.

[20, 10, 3].sort()
> [3, 10, 20]

declare Keyword and Access Modifiers

The declare keyword in TypeScript tells the compiler that a variable inside the declare block already exists and TS doesn’t need to compile it to JavaScript.

So say you add a reference to your codebase from a third-party domain that the TS compiler knows nothing about; it would immediately throw an error. The declare block assures the compiler that the variable does exist and has a type but does not transpile as a JavaScript output.

However, when compiled, a declare block in AssemblyScript will result in WebAssembly imports that are added when the module is instantiated by JavaScript:

Creating an import namespace in AssemblyScript :

declare namespace Math {
  function add(a: f64, b: f64): f64;
}

And when running it in JavaScript:

import { instantiateStreaming } from "assemblyscript/lib/loader";

const AssemblyScript = await instantiateStreaming(fetch("../build/output.wasm"), {
  Math : { 
    add(a, b){ 
    return a + b;
    } 
  }
});

Many of the features in AssemblyScript are still in beta or planned for future releases on their product roadmap.

AssemblyScript partial roadmap

As of now, AssemblyScript does not enforce keyword class modifiers such as:

  • public
  • private
  • protected (private modifier does prevent it from be publically accessible)

WebAssembly Features

AssemblyScript features depend on the WebAssembly specification, and the future roadmap for implementation is highly ambitious. As mentioned earlier, WebAssembly is a new concept for the web ecosystem, and so is AssemblyScript. As of now, AssemblyScript provides enough to get you started on your WebAssembly journey.

Features that are already available include:

  • Classes and Interfaces: Class-based syntax works as expected, however, there are few caveats. For example, private and protected are not enforced, but this leniency will get restrained in future releases. Similarly, interface is nonexistent in AssemblyScript as of now, but the roadmap does include getters and setters.
  • Garbage Collection: The official WebAssembly proposal for garbage collection is still in the works, meanwhile, the garbage collector built into AssemblyScript works on top of Wasm’s linear memory with several variants available for different use cases.
  • Interop with JavaScript: AssemblyScript provides a loader that translates JavaScript objects into WebAssembly memory (and vice versa), making working with AssemblyScript modules possible without running into performance issues.
  • Standard Library: The difference between TypeScript’s dynamic to AssemblyScript's static typing has resulted in a different standard library. At a very high level, AssembyScript does things as they were in TypeScript but is more restrictive in terms of assignments and memory allocation.

Features in TypeScript such as closures and iterators are not available yet. AssemblyScript also needs Exception Handling; as of now, throwing an exception immediately terminates the program.

Handling Null

null type in TypeScript can be daunting to work with.

TypeScript developers sometimes assign a data variable to null:

const data: number | null;
const foo: null

But this is discouraged in AssemblyScript, and it does throw an error whenever we try to assign null to a basic data type. Only classes and functions can be nullable.

function bar(value : i32) : null {
// logic
}

There’s no support for undefined or void or any types. A few situations where you need compile-time/runtime type checking can be dealt with by the instanceof keyword.

Comparison

== and === are comparison operators in TypeScript. == coerces the comparison, which is why 2 == "2" -> true in JavaScript, while === doesn’t. ECMAScript recommends === for comparison to avoid any unwanted quirks.

In AssemblyScript, there isn't a concept of "triple equals” for comparing data types. Interestingly, === in AS is used to compare objects and returns true only when two objects are strictly the same. The de facto method of comparison in AssemblyScript is just ==.

Conclusion

As the ecosystem around WebAssembly matures, AssemblyScript has a roadmap that depends heavily on its evolving specification, narrowing the gap between TypeScript and WebAssembly. While AssemblyScript is somewhat new on the web, the whole idea around rethinking high-performance web development can have a huge impact. Shifting away from the dynamic characteristics of JavaScript can not only improve web performance by a huge margin, it also makes the web future-ready for 3D rendering, AR, VR, and compute-intensive tasks.

Suborbital builds frameworks and tools to help you ship software faster, without compromising on security or developer experience. It simplifies building web services in a number of languages by taking advantage of everything WebAssembly has to offer.

Cover photo by James Donovan on Unsplash