Type safe JQuery with ST-JS

by Eyal
August 10, 2011

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)

	…