Link to wealthfront.com

Fork me on GitHub

Wednesday, August 10, 2011

Type safe JQuery with ST-JS

At Wealthfront some engineers believe that “A well-typed program never goes wrong” and that's why we like statically typed languages (we mainly use Java for our back-end). We also appreciate the expressiveness of dynamically typed languages and the power of functional programming (we use JRuby and Javascript in the front-end).

Scala and Clojure seem to be good candidates for offering type-safe functional programming on the JVM, however they lack the excellent IDE integration which exists for Java. Therefore we're using Google guava in our Java back-end which is a good but verbose solution (JDK 8 lambda expressions will probably help).

I recently had to write some javascript front-end related code, and was looking for a solution with the following capabilities:

  • type-safe

  • functional

  • JQuery compatible

  • excellent IDE integration (refactoring, auto-completion, ...)

I could not find any existing solution (even though a long list of languages compile to Javascript). At the same time, one of my friend started working on this Java/JQuery/Eclipse/Maven project that leverages the type-safety and Eclipse integration of Java, with the convenience and functional style of JQuery. The project is named ST-JS (Statically typed Javascript) and is in the process of being open sourced. We're only using ST-JS for prototyping at that point, no production code yet.
All you need to install ST-JS is to add maven dependencies to your project in Eclipse, and the compiler will start converting your code each time you save a file (it is relying on the incremental compilation feature of JDT and m2e).
Let's start with a simple snippet:

public class Example1 {
static String helloWorld() {
return "Hello World!";
}
}

Generated Javascript:
Example1 = function(){}

Example1.helloWorld = function() {
return "Hello World!";
}

Pretty straightforward, huh? No let's take a look at a some code that deals with JQuery and DOM elements:

import static org.stjs.javascript.jquery.GlobalJQuery.$;
import org.stjs.javascript.jquery.Event;
import org.stjs.javascript.jquery.EvtHandler;
public class Example2 {
static void bindClickOnButton() {
$("#ex-button").click(new EvtHandler() {
public void onEvent(Event ev) {
$("p.neat").addClass("ohmy").show("slow");
}
});
}
}

Generated Javascript:

Example2 = function(){}
Example2.bindClickOnButton = function() {
$("#ex-button").click(function(ev) {
$("p.neat").addClass("ohmy").show("slow");
});
}

Note that ST-JS comes with a set of utility classes which allow you to write Java code that looks a lot like what you would have written in Javascript. It defines global variables available in browsers (window , document,...) as well as global functions (toFixed, ….). ST-JS comes with JQuery integration, but you can integrate any existing library.
If you have been reading this blog for a while, you can probably guess what's next.... Testing! ST-JS comes with a NodeJS integration package (js-test-driver will come later). While you write JUnit-tests, the generated code is executed in NodeJS and failures are reported as JUnit failures:

import static junit.framework.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(NodeJSTestRunner.class)
public class UnitTestExample {
static class MyPojo {
private String y;
public MyPojo(String y) {
this.y = y;
}
}
@Test
public void shouldCompainIfFooIsNotBar() throws Exception {
MyPojo pojo = new MyPojo("Foo");
assertEquals("bar", pojo.y);
}
}

yields:

junit.framework.AssertionFailedError: Expected bar got Foo
at org.stjs.testing.NodeJSTestRunner$1.evaluate(NodeJSTestRunner.java:60)