Installation

  • On Windows, run setup.exe
  • On Mac OS, copy the js++ binary (or js++-x32 binary for 32-bit systems*) to the /usr/local/bin/ directory and chmod +x the binary file. If you receive an error that the developer cannot be verified when using the compiler, please check your system security settings.
  • On Linux, copy the js++ binary to the /usr/local/bin/ directory and chmod +x the binary file.

* For macOS Catalina (released Oct 7, 2019), 32-bit applications are no longer supported. In this case, your only option is to use the 64-bit binary.

Hello World - Your First JS++ Program

Create a new file named Hello.jspp and enter the following code:

import System;

Console.log("Hello World!");

In Windows, right-click and select "Execute with JS++":

For Mac and Linux, use a terminal emulator and navigate to the directory containing Hello.jspp. Compile the file using the js++ command with the --execute flag to run the code after compilation:

> js++ --execute Hello.jspp

If everything worked correctly, you will see the message "Hello World!" outputted. This will be a message box saying "Hello World!" on Windows and a line of text in your console saying "Hello World!" on Mac and Linux.

Using JavaScript from JS++

One of the benefits of using JavaScript from JS++ instead of regular JavaScript is that JS++ provides "type guarantees". In other words, your types are guaranteed to be correct at both compile time and runtime even when you're polluting your JS++ code with "untyped" JavaScript libraries. In this tutorial, you will find out exactly what a "type guarantee" is and see how it works at 1) compile time, and 2) runtime.

The JavaScript library we will be working with today is a simple library that converts RGB (Red, Green, Blue) color codes to hexadecimal color codes.

Step 1 Create RGBtoHex.js

Create a file named RGBtoHex.js and copy and paste the code from below:

(Don't worry. You don't need to understand this code. We're only going to use it as an example.)

function rgbToHex(red, green, blue) {
    if (typeof red !==  "number")   throw new TypeError("Expected numeric 'red' value");
    if (typeof green !==  "number") throw new TypeError("Expected numeric 'green' value");
    if (typeof blue !==  "number")  throw new TypeError("Expected numeric 'blue' value");

    if (red < 0   || red > 255)     throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
    if (green < 0 || green > 255)   throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
    if (blue < 0  || blue > 255)    throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

Step 2 The JS++ Main File

Create a file named main.jspp and write the following code:

external alert;
external rgbToHex;

alert(rgbToHex(255, 255, 255));

Compile main.jspp. On Windows, right-click and select "Compile with JS++". On Mac/Linux, the command is:

> js++ main.jspp

If everything worked, you should have a main.jspp.js file generated in the same directory.

In JS++, all you need to do in order to start using JavaScript libraries is to declare them with the external statement. You can think of the external statement as an "import" statement for JavaScript libraries. The only pre-requisite is that the JavaScript library can be accessed from the global scope. So all you need to do in order to access JavaScript from JS++ is A) make sure the JavaScript variable/function can be accessed from the global scope, and B) declare the variable or function name as external in JS++.

You never need to have "type definition files", .d.ts files, or anything else for JS++. All you need to do is declare as external.

Step 3 Create the HTML File and Run

Create a basic index.html file, and paste this:

<!DOCTYPE html>
<head><title>RGBtoHex</title></head>
<body>
<script type="text/javascript" src="rgbtohex.js"></script>
<script type="text/javascript" src="main.jspp.js"></script>
</body>
</html>

Open index.html. You should see a message box with the message "#ffffff".

Wasn't that easy? Now it gets better...

Type Guarantees

"TypeScript's 'type erasure' vs JS++'s 'type guarantee' would be a big reason to use JS++ over TypeScript."

Andrew Osman, CodeProject

What are "Type Guarantees"?

"Type guarantees" mean your types are guaranteed to be correct at both compile time and runtime even when you're polluting your JS++ code with "untyped" JavaScript libraries.

Here is a very simple analogy to understand JS++. You have two languages: English and French. The English speaker does not know French, and the French speaker does not know English. However, when they communicate, they always understand each other. This is because they're using a translation software that is always 100% correct. That's the JS++ type system. When you write English (JS++) and you want to use a library written in French (JavaScript), JS++ performs the translation and guarantees (via type guarantees) that the translation will always be correct.

When you use keywords like var, function, and external, these are known as "external types" and are basically "untyped" and unchecked. Basically, you're still writing JavaScript. When you start using JS++ types like int, unsigned int, string, and byte, you are using "internal types" which are guaranteed to be correct. You will never get an unsigned int that is -1 (because "unsigned" can never be negative). You will also never get an int or string that is NaN. (In fact, in JS++, NaN errors are a thing of the past... except where they are expected and allowed.)

When "external types" try to enter the territory of "internal types", a translation happens (via "conversions").

Here's an illustration. Red zones are typed as "external" (JavaScript) and green zones are typed as "internal" (JS++):

external rgbToHex;

string hex = rgbToHex(255, 255, 255);

Since we declared rgbToHex as external, we know its type is external. string is a JS++ type so it is internal, and we know we are guaranteed to get a string. However, since the external value from the function call rgbToHex(255, 255, 255) gets assigned to the internal variable hex (typed as string), we know we need to perform a translation/conversion, so we convert the result of the rgbToHex function call to string at runtime if a valid conversion exists.

Context: RGB Values

We know RGB values can only range from 0 to 255. In JS++, this is the byte data type. In JS++, you don't just declare types for fun, for "hints", or whatever. When you declare types in JS++, it can change the semantics and runtime behavior of your application. Therefore, if we say RGB expects byte values that range from 0 to 255, we are guaranteed that these values will be 0 to 255 at compile and runtime. At compile time, the compiler will reject your program and raise a compile error if you try to pass an int when a byte was expected. At runtime, a byte will never be 256 (an invalid RGB value), just like an unsigned int will never be -1. Thus, in practice, we begin to see the power of JS++. In JavaScript, you'd have to throw a runtime exception. Let's inspect those runtime exceptions from our RGBtoHex example...

Inspecting the Untyped JavaScript

If we examine RGBtoHex.js, there are six lines of code that interest us. Here are the first three interesting lines:

if (typeof red !==  "number")   throw new TypeError("Expected numeric 'red' value");
if (typeof green !==  "number") throw new TypeError("Expected numeric 'green' value");
if (typeof blue !==  "number")  throw new TypeError("Expected numeric 'blue' value");

This is quite common in JavaScript. We are checking the inputted types are correct at runtime. For example, we don't want RGB values that might be Booleans or null.

Here are the other three interesting lines:

if (red < 0   || red > 255)     throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
if (green < 0 || green > 255)   throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
if (blue < 0  || blue > 255)    throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

RGB values can only range from 0 to 255. The function is checking that we did not input an invalid value of 256, 257, 258, ... or -1, -2, etc.

Why would we want an application that can potentially crash at runtime with an exception to do these checks? With JS++, we can check all of these at compile time. If we declared an int, it means the value can be higher than 255 and less than 0. Therefore, an int should be forbidden from being passed to a byte to guarantee your bytes remain within the 0-255 range. This is a compile-time guarantee you can only obtain through a "sound" type system, which we will not go into detail or discuss here. If a type system is "unsound" (e.g. TypeScript, Flow), you do not get a guarantee.

If you need to know the values allowed for each type, simply check the documentation:

byte
int
unsigned int

Types like int allow negative numbers and numbers much higher than 255, so we definitely don't want that. We also see that unsigned int does not allow negative numbers but allows numbers much higher than 255. We don't want this either. After reading the documentation, we know we want to use the byte data type for RGB values because byte limits numbers to exactly the range 0 to 255. This is a perfect fit for RGB values. (When using integer types, you may not always get a "perfect fit" like we do here. Use the best type for your situation.)

Step 1 Using JavaScript More Safely

With JS++, you can use JavaScript functions more safely than you can with JavaScript itself. Let's re-write our main.jspp code to use the byte type:

external alert;
external rgbToHex;

byte red = 255;
byte green = 255;
byte blue = 255;
alert(rgbToHex(red, green, blue));

Compile and run the code. The result is the exact same. However, you are now guaranteed to only send whole number values ranging from 0 to 255 to the rgbToHex function. Remember all of those checks and runtime exceptions? All of these errors have been wiped out and prevented just by using JS++ and declaring your types (even though we didn't change the original JavaScript and those exceptions are technically still there, but they will never execute because our input values will always be in range). rgbToHex can now never crash or throw an exception at runtime... because all of it was checked at compile time first. We never modified the original JavaScript, all we did was use the JavaScript from JS++, and it automatically became safer.

Let's verify that it was checked. Try changing one of the red, green, or blue values to an invalid RGB value such as 256, 257, or -1. What happens?

[  ERROR  ] JSPPE5013: Computed value `-1' is out of range for type `byte' at line 4 char 11 at main.jspp

Let's try another experiment. Declare an int variable and try to assign it to red, green, or blue. What happens?

external alert;
external rgbToHex;

int x = 1;
byte red = x;
byte green = 255;
byte blue = 255;
alert(rgbToHex(red, green, blue));

[  ERROR  ] JSPPE5016: Cannot convert `int' to `byte'. A cast is available at line 5 char 11 at main.jspp

These are compile-time type guarantees. We cannot assign values like -1 or pass an int variable where a byte value was expected. By having the JS++ compiler detect these errors early, it can never happen at runtime.

However, we can actually re-write the entire rgbToHex function using JS++ to increase type safety and make the function more re-usable.

Step 2 Move rgbToHex to JS++

In main.jspp, remove the dependency on the JavaScript version of rgbToHex by removing the external statement:

external alert;

alert(rgbToHex(255, 255, 255));

The code will no longer compile because rgbToHex is not defined. We're going to re-define it in JS++.

Copy the rgbToHex function from RGBtoHex.js and paste it in JS++:

external alert;
external TypeError, RangeError;

function rgbToHex(red, green, blue) {
    if (typeof red !==  "number")   throw new TypeError("Expected numeric 'red' value");
    if (typeof green !==  "number") throw new TypeError("Expected numeric 'green' value");
    if (typeof blue !==  "number")  throw new TypeError("Expected numeric 'blue' value");

    if (red < 0   || red > 255)     throw new RangeError("Expected 'red' value between 0 and 255 (inclusive)");
    if (green < 0 || green > 255)   throw new RangeError("Expected 'green' value between 0 and 255 (inclusive)");
    if (blue < 0  || blue > 255)    throw new RangeError("Expected 'blue' value between 0 and 255 (inclusive)");

    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

Compile the code. Notice JS++ really is just JavaScript with more features. We just copied a blob of JavaScript code into JS++, and it still compiles.

However, we still haven't converted this "untyped" JavaScript function to JS++.

Step 3 Converting "Untyped" JavaScript to "Typed" JS++

First, let's remove all the runtime checks and errors. You don't need them anymore. In JS++, you will have all of this checked by the compiler before you ever ship to your users and visitors.

external alert;

function rgbToHex(red, green, blue) {
    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

However, until we add types, we now have unchecked code! We can easily pass 256 as one of the RGB values into the function. Let's fix that.

At the top of main.jspp, add:

import System;

Now that we've done our imports, simply add byte in front of each parameter to restrict its type:

import System;

external alert;

function rgbToHex(byte red, byte green, byte blue) {
    var r = red.toString(16);
    var g = green.toString(16);
    var b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

Run the application in your browser. You should still see a message box with the message #ffffff.

That's all you really have to do. Adding types to JS++ is as simple as changing var to int or adding byte in front of a parameter.

Does It Work?

Now, try and call the function by changing one of the arguments to an invalid value such as 256 or -1. Your error will be detected before you ever ship to your users and customers:

alert(rgbToHex(-1, 255, 255));

[  ERROR  ] JSPPE5024: No overload for `rgbToHex' matching signature `rgbToHex(int, int, int)' at line 17 char 6 at main.jspp

Once again, we encounter a compile-time type guarantee. In other words, the compiler detected you tried to input -1 where a byte was expected. You need to fix all the errors the compiler detects before it will "compile" your application.

However, your types are also guaranteed to be correct at runtime. When we use the function keyword, which comes from JavaScript, the return value of our function still remains "untyped" JavaScript. What happens if you try this?

int runtimeGuarantee = rgbToHex(255, 255, 255);
alert(runtimeGuarantee);

You will see the value is 0 (zero). Even though the value returned from the function was "#ffffff", this is not a valid value for int. Therefore, at runtime, JS++ simply tries to convert to the best valid value. Notice that, unlike JavaScript, JS++ did not produce NaN — which is not a valid int value. (Once again, with JS++, NaN is a problem of the past.)

However, this runtime conversion should never have needed to happen in the first place if we simply declared all of our types so the compiler can detect this error for us before it happens at runtime. Let's do that...

Step 4 Finishing Touches

We know that the final hexadecimal representation will be a string. Change the return type to a string instead of the untyped JavaScript function return type. Once you do this, you will notice that the compiler will no longer compile the runtimeGuarantee snippet from above and instead detect the error for you at compile time.

Additionally, even though this tutorial has kept using the word "untyped" JavaScript, JavaScript actually does have types. Even JavaScript programmers know they cannot subtract the value 100 from "abc" without getting an error value like NaN. One of the observations that led to the invention of the JS++ type system is that we "intuitively" know the types. For example, when you see code like this:

var r = red.toString(16);
var g = green.toString(16);
var b = blue.toString(16);

It doesn't take long before you realize these are all string variables due to the toString() calls. If we want more compiler checking (and fewer runtime errors), we should declare our types. Therefore, the final code looks like this:

import System;

external alert;

string rgbToHex(byte red, byte green, byte blue) {
    string r = red.toString(16);
    string g = green.toString(16);
    string b = blue.toString(16);

    while (r.length < 2) r = "0" + r;
    while (g.length < 2) g = "0" + g;
    while (b.length < 2) b = "0" + b;

    return "#" + r + g + b;
}

alert(rgbToHex(255, 255, 255));

By using JS++ instead of JavaScript, your application will be safer, faster, more reliable, and higher-quality.

Further Reading

Want to learn more about how to write small portions of your application in JS++ as you get your feet wet? Read this tutorial (slightly advanced): Writing JavaScript Libraries using JS++

Now that you have a basic understanding of JS++, feel free to read the full tutorial (the most important chapters are Chapters 8, 9, and 11 if you already know object-oriented programming) or consult the documentation. If you're a complete beginner, read the full tutorial, with special emphasis on Chapters 8, 9, and 11.