Productive Rage

Dan's techie ramblings

Writing a Brackets extension in TypeScript, in Brackets

For a while now, I've been meaning to try writing a TypeScript extension for Adobe Brackets - I like the editor, I like the fact that extensions are written in JavaScript, I like TypeScript; it seemed like an ideal combination!

But to really crank it up, I wanted to see if I could put Visual Studio aside for a while (my preferred editor for writing TypeScript) and trying writing the extension for Brackets with Brackets. I'd written an extension before and I was sure that I'd heard about some sort of extension for Brackets to support TypeScript, so I got stuck in..

Teaching Brackets about TypeScript

The short answer is that this is possible. The slightly longer answer is that it's possible but with a bit of work and the process is a bit rough around the edges.

What I'm using for editing is the extension brackets-typescript, which appears as "Brackets TypeScript" when you search for "TypeScript" in the Extension Manager. It's written by fdecampredon (whose work I also relied upon last year for "Writing React components in TypeScript" - a busy guy!).

This is the best extension for TypeScript but the TypeScript version is out of date in the released version of the extension - it doesn't yet use 1.4 and so some nice features such as union types and const enums are not available. The GitHub code has been updated to use 1.4.1, but that version of the extension has not been released yet. I contacted the author and he said that he intends to continue work on the extension soon but he's been sidelined with a pull request for the TypeScript Team to handle React's JSX format (see JSX Support #2673 - like I said, he's a busy guy :)

I tried cloning the repo and building it myself, but one of the npm dependencies ("typescript-project-services") is not available and I gave up.

So, for now, I'm having to live with an old version of the TypeScript compiler for editing purposes. I've been unable to determine precisely what version is being used, I tried looking through the source code but couldn't track it down. I suspect it's 0.9 or 1.0 since it supports generics but not the changes listed for 1.1 in the TypeScript Breaking Changes documentation.

Another gotcha with this extension is that it does not appear to work correctly if you directly open a single TypeScript file. Occasionally it will appear to work but the majority of the time you will not get any intellisense or other features, even if you have the expected ".brackets.json" file (see below) alongside the file or in a parent folder. The way that you can get this to work is to decide where the base folder for your work is going to be, to put the ".brackets.json" file in there and then to open that folder in Brackets. Then you can add / open individual files within that folder as required and the TypeScript integration will work. I couldn't find this documented or described anywhere, and came to this conclusion through trial-and-error*.

* Maybe this is the common workflow for people who use Brackets a lot; maybe I'm the strange one that goes around opening individual files ad hoc all the time..?

The other thing you need is a ".brackets.json" file alongside your source to specify some configuration for the extension.

If you're creating an extension of your own, I would recommend a basic folder structure of

/build

/src

where the TypeScript files live within "src". And so "src" is the folder that would be opened within Brackets while writing the extension, and is also the folder in which to place the following ".brackets.json" file:

{
    "typescript": {
        "target": "ES5",
        "module": "amd",
        "noImplicitAny": true,
        "sources" : [
            "**/*.d.ts",
            "**/*.ts"
        ]
    }
}

For a Brackets extension, supporting ES5 (rather than ES3) and using the "AMD" module loading mechanism make sense (and are consistent with the environment that Brackets extensions operate in). Setting "noImplicitAny" to "true" is a matter of taste, but I think that the "any" concept in TypeScript should always be explicitly opted into since you're sacrificing compiler safety, which you should only do intentionally.

So now we can start writing TypeScript in Brackets! But we are far from done..

Teaching TypeScript about Brackets

The next problem is that there don't appear to be any TypeScript defintions available for writing Brackets extensions.

What I particularly want to do with my extension is write a linter for less stylesheets. In order to do this, I need to do something such as:

var AppInit = brackets.getModule("utils/AppInit"),
    CodeInspection = brackets.getModule("language/CodeInspection");

function getBrokenRuleDetails(text: string, fullPath: string) {
    var errors = [{
        pos: { line: 4, ch: 0 },
        message: "Example error on line 5",
        type: CodeInspection.Type.ERROR
    }];
    return { errors: errors }
}

AppInit.appReady(() => {
    CodeInspection.register(
        "less",
        { name: "Example Linting Results", scanFile: getBrokenRuleDetails }
    );
});

This means that TypeScript needs to know that there is a module "brackets" available at runtime and that it has a module-loading mechanism based upon strings identifiers (such as "utils/AppInit" and "language/CodeInspection"). For this, a "brackets.d.ts" needs to be created in the "src" folder (for more details than I'm going to cover here, see my post from earlier in year: Simple TypeScript type definitions for AMD modules).

Conveniently, TypeScript has the ability to "Overload on Constants", which means that a method can be specified with different return types for known constants for argument(s). This is an unusual feature (I can't immediately think of another statically-typed language that supports this; C# definitely doesn't, for example). The reason that it exists in TypeScript is interoperability with JavaScript. The example from the linked article is:

interface Document {
    createElement(tagName: string): HTMLElement;
    createElement(tagName: 'canvas'): HTMLCanvasElement;
    createElement(tagName: 'div'): HTMLDivElement;
    createElement(tagName: 'span'): HTMLSpanElement;
    // + 100 more
}

This means that "Document.createElement" is known to return different types based upon the "tagName" value. It's clear how it is useful for "createElement" (since different node types are returned, based upon the tagName) and it should be clear how it will be helpful here - the "brackets.getModule" function will return different types based upon the provided module identifier.

I'm a long way from having a comprehensive type definition for Brackets' API, I've written just enough to integrate with it's linting facilities. The type definition required for that is as follows:

declare module brackets {
    function getModule(name: "utils/AppInit"): AppInit;
    function getModule(name: "language/CodeInspection"): CodeInspection;
    function getModule(name: string): void;

    interface AppInit {
        appReady: (callback: () => void) => void;
    }

    interface CodeInspection {
        register: (extension: string, lintOptions: LintOptions) => void;
        Type: CodeInspectionTypeOptions
    }

    interface LintOptions {
        name: string;
        scanFile: (text: string, fullPath: string) => LintErrorSet
    }

    interface LintErrorSet { errors: LintErrorDetails[] }

    interface LintErrorDetails {
        pos: { line: number; ch: number };
        message: string;
        type: string
    }

    interface CodeInspectionTypeOptions {
        WARNING: string;
        ERROR: string
    }
}

The "Overload on Constants" functionality has a limitation in that a method signature is required that does not rely upon a constant value, so above there is a "getModule" method that handles any unsupported module name and returns void. It would be nice if there was a way to avoid this and to only define "getModule" methods for known constants, but that is not the case and so a void-returning "catch all" variation must be provided.

There is another limitation that is unfortunate. The LintErrorDetails interface has had to be defined with a string "type" property, it would have been better if this could have been an enum. However, the constants within Brackets are within the "CodeInspection" module - eg.

CodeInspection.Type.ERROR

The "CodeInspection" reference is returned from a "getModule" call and so must be an interface or class, and an enum may not be nested within an interface or class definition. If "CodeInspection" was identified as a module then an enum could be nested in it, but then the getModule function definition would complain that

Type reference cannot refer to container 'brackets.CodeInspector'

.. which is a pity. So the workaround is to have LintErrorDetails take a string "type" property but for a non-nested enum to be exposed from "CodeInspection" that may be used for those values. So it's valid to define error instances with the following:

var errors = [{
    pos: { line: 4, ch: 0 },
    message: "Example error on line 5",
    type: CodeInspection.Type.ERROR
}];

but unfortunately it's also valid to use nonsense string "type" values, such as:

var errors = [{
    pos: { line: 4, ch: 0 },
    message: "Example error on line 5",
    type: "BlahBlahBlah"
}];

Compile-on-save

So, at this point, we can actually start writing a linter extension in TypeScript. However, the Brackets TypeScript extension doesn't support compiling this to JavaScript. So we can write as much as we like, it's not going to be very useful!

This is another to-do item for the Brackets TypeScript extension (according to a discussion on CodePlex) and so hopefully the following will not be required forever. However, right now, some extra work is needed..

The go-to solution for compiling TypeScript seems to be to use Grunt and grunt-ts.

If you have npm installed then this is fairly easy. However there are - again - some gotchas. In the "grunt-ts" readme, it says you can install it using

npm install grunt-ts

"in your project directory". I would recommend that this "project directory" be the root where the "src" and "build" folders that I suggested live. However, when I tried this, it created the "grunt-ts" folder in a "node_modules" folder in a parent a couple of levels up from the current directory! Probably I'd done something silly with npm. But a way to avoid this is to not specify npm packages individually at the command line and to instead create a "package.json" file in your project root (again, I'm referring to the folder that contains the "src" and "build" folders) - eg.

{
    "name": "example.less-linter",
    "title": "Example LESS Linter",
    "description": "Extension for linting LESS stylesheets",
    "version": "0.1.0",
    "engines": {
        "brackets": ">=0.40.0"
    },
    "devDependencies": {
        "grunt-ts": ">= 4.0.1",
        "grunt-contrib-watch": ">= 0.6.1",
        "grunt-contrib-copy": ">= 0.8.0"
    }
}

This will allow you to run

npm install

from the project folder and have it pull in everything you'll need into the appropriate locations.

The plan is to configure things such that any TypeScript (or TypeScript definition) file change will result in them all being re-compiled and then the JavaScript files copied into the "build" folder, along with this package.json file. That way, the "build" folder can be zipped up and distributed (or dropped into Bracket's "extensions" folder for immediate testing).

Here's the "gruntfile.js" that I use (this needs to be present in the project root, alongside the "package.json" file and "src" / "build" folders) -

/*global module */
module.exports = function (grunt) {
    "use strict";
    grunt.initConfig({
        ts: {
            "default": {
                src: ["src/**/*.d.ts", "src/**/*.ts"]
            },
            options: {
                module: "amd",
                target: "es5",
                sourceMap: true,
                noImplicitAny: true,
                fast: "never"
            }
        },
        copy: {
            main: {
                files: [
                    { expand: true, cwd: "src/", src: ["**.js"], dest: "build/" },
                    { src: ["package.json"], dest: "build/" }
                ]
            }
        },
        watch: {
            scripts: {
                files: ["src/**/*.d.ts", "src/**/*.ts"],
                tasks: ["ts", "copy"],
                options: { spawn: false }
            }
        }
    });

    grunt.loadNpmTasks("grunt-contrib-watch");
    grunt.loadNpmTasks("grunt-contrib-copy");
    grunt.loadNpmTasks("grunt-ts");

    grunt.registerTask("default", ["ts", "copy", "watch"]);
};

There is some repeating of configuration (such as "es5" and "amd" TypeScript options) since this does not share any configuration with the Brackets TypeScript extension. The idea is that you fire up Brackets and open the "src" folder of the extension that you're writing. Then open up a command prompt and navigate to the project directory root and execute Grunt. This will compile your current TypeScript files and copy the resulting JavaScript from "src" into "build", then it will wait until any of the ".ts" (or ".d.ts") files within the "src" folder are changed and repeat the build & copy process.

It's worth noting that grunt-ts has some file-watching logic built into it, but if you want the source and destination folders to be different then it uses a hack where it injects a ".basedir.ts" file into the source, resulting in a ".basedir.js" in the destination - which I didn't like. It also doesn't support additional actions such as copying the "package.json" from the root into the "build" folder. The readme for grunt-ts recommends using grunt-contrib-watch for more complicated watch configurations, so that's what I've done.

One other issue I had with grunt-ts was with its "fast compile" option. This would always work the first time, but subsequent compilations would seem to lose the "brackets.d.ts" file and so claim that "brackets" was not a known module. This was annoying but easy to fix - the gruntfile.js above sets ts.options.fast to "never". This may mean that the compilation step will be a bit slower, but unless you're extension is enormous then this shouldn't be an issue.

Final tweaks

And with that, we're basically done! We can write TypeScript against the Brackets API (granted, if you want to use more functions in the API than I've defined then you'll have to get your hands dirty with the "brackets.d.ts" file) and this code can be compiled into JavaScript and copied into a "build" folder along with the package definition.

The only other thing I'd say is that I found the "smart indenting" in Brackets to be appalling with TypeScript - it moves things all over the place as you go from one line to another! It's easily disabled, though, thankfully. There's a configuration file that needs editing - see the comment by "rkn" in Small little Adobe Brackets tweak – remove Smart Indent. Once you've done this, you don't need to restart Brackets; it will take effect immediately.

And now we really are done! Happy TypeScript Brackets Extension writing! Hopefully I'll have my first TypeScript extension ready to release in an early state soon.. :)

(For convenience junkies, I've created a Bitbucket repo with everything that you need; the "Example TypeScript Brackets Extension").

Posted at 22:50

Comments

Simple TypeScript type definitions for AMD modules

I wanted to write a TypeScript type definition file for a JavaScript module that I wrote last year, so that I could use it from within TypeScript in a seamless manner - with argument and return type annotations present. I considered porting it to TypeScript but since all that I really wanted was the type annotations, it seemed like a type definition file would be just the job and prevent me from maintaining the code in two languages (well, three, actually, since I originally ported it from C#).

The module in question is the CSS Parser that I previously wrote about porting (see The C# CSS Parser in JavaScript), it's written to be referenced directly in the browser as a script tag or to be loaded asynchronously (which I also wrote about in JavaScript dependencies that work with Brackets, Node and in-browser).

I wanted to write a type definition to work with the AMD module loading that TypeScript supports. And this is where I came a bit unstuck.

I must admit that, writing this now, it seems that nothing that I'm about to cover is particularly complicated or confusing - it's just that when I tried to find out how to do it, I found it really difficult! The DefinitelyTyped GitHub repo seemed like it should be a good start, since surely it would cover any use case I could thing of.. but it was also difficult to know where to start since I couldn't think of any packages that I knew, that would support AMD and whose type definitions would be small enough that I would be able to understand them by just trying to stare them down.

There is an official TypeScript article that is commonly linked to by Stack Overflow answers: Writing Definition (.d.ts) Files, but this seems to take quite a high level view and I couldn't work out how to expose my module's functionality in an AMD fashion.

The short answer

In my case, I basically had a module of code that exposed functions. Nothing needed to be instantiated in order to calls these functions, they were just available.

To reduce it down to the most simple case, imagine that my module only exposed a single function "GetLength" that took a single parameter of type string and returned a value of type number, the type definition would then be:

declare module SimpleExample {
  export function GetLength(content: string): number;
}

export = SimpleExample;

This allows for the module to be used in TypeScript elsewhere with code such as

import simpleExample = require("simpleExample");

console.log(simpleExample.GetLength("test"));

So easy! So straight-forward! And yet it seemed like it took me a long time to get to this point :(

One of the problems I struggled with is that there are multiple ways to express the same thing. The following seemed more natural to me, in a way -

interface SimpleExample {
  GetLength(content: string): number;
}

declare var simpleExampleInstance: SimpleExample;
export = simpleExampleInstance;

It is common for a module to build up an instance to export as the AMD interface and the arrangement above does, in fact, explicitly describe the module as containing an instance that implements a specified interface. This interface is what consuming TypeScript code will work against.

Side note: It doesn't matter what name is given to "simpleExampleInstance" since it is just a variable that is being directly exported.

In this simple case, the TypeScript example still works - the module may be consumed and the "GetLength" method may be called as expected. It is only when things become more complicated (as we shall see below) that this approach becomes troublesome (meaning we will see that the "declare module" approach turns out to be a better way to do things).

Implementation details

So that's a simple example, now to get back to the case I was actually working on. The first method that I want to expose is "ParseCss". This takes in a string and returns an array of "categorised character strings" - these are strings of content with a "Value" string, an "IndexInSource" number and a "CharacterCategorisation" number. So the string

body { color: red; }

is broken down into

Value: "body", IndexInSource: 0, CharacterCategorisation: 4
Value: " ", IndexInSource: 4, CharacterCategorisation: 7
Value: "{", IndexInSource: 5, CharacterCategorisation: 2
Value: " ", IndexInSource: 6, CharacterCategorisation: 7
Value: "color", IndexInSource: 7, CharacterCategorisation: 4
Value: ":", IndexInSource: 12, CharacterCategorisation: 5
Value: " ", IndexInSource: 13, CharacterCategorisation: 7
Value: "red", IndexInSource: 14, CharacterCategorisation: 6
Value: ";", IndexInSource: 17, CharacterCategorisation: 3
Value: " ", IndexInSource: 18, CharacterCategorisation: 7
Value: "}", IndexInSource: 19, CharacterCategorisation: 1

The CharacterCategorisation values come from a enum-like type in the library; an object named "CharacterCategorisationOptions" with properties named "Comment", "CloseBrace", "OpenBrace", etc.. that are mapped onto numeric values. These values are an ideal candidates for representation by the TypeScript "const enum" construct - and since there are a fixed set of values it's no problem to explicitly include them in the type definition. (Note that the "const enum" was introduced with TypeScript 1.4 and is not available in previous versions).

This leads to the following:

declare module CssParser {
  export function ParseCss(content: string): CategorisedCharacterString[];

  export interface CategorisedCharacterString {
    Value: string;
    IndexInSource: number;
    CharacterCategorisation: CharacterCategorisationOptions;
  }

  export const enum CharacterCategorisationOptions {
    Comment = 0,
    CloseBrace = 1,
    OpenBrace = 2,
    SemiColon = 3,
    SelectorOrStyleProperty = 4,
    StylePropertyColon = 5,
    Value = 6,
    Whitespace = 7
  }
}

export = CssParser;

This is the first point at which the alternate "interface" approach that I mentioned earlier starts to fall apart - it is not possible to nest the enum within the interface, TypeScript will give you compile warnings. And if it is not nested within the interface then it can't be explicitly exported from the module and could not be be accessed from calling code.

To try to make this a bit clearer, what we could do is

interface CssParser {
  ParseCss(content: string): CategorisedCharacterString[];
}

interface CategorisedCharacterString {
  Value: string;
  IndexInSource: number;
  CharacterCategorisation: CharacterCategorisationOptions;
}

declare const enum CharacterCategorisationOptions {
  Comment = 0,
  CloseBrace = 1,
  OpenBrace = 2,
  SemiColon = 3,
  SelectorOrStyleProperty = 4,
  StylePropertyColon = 5,
  Value = 6,
  Whitespace = 7
}

declare var parser: CssParser;
export = parser;

and then we could consume this with

import parser = require("cssparser/CssParserJs");

var content = parser.ParseCss("body { color: red; }");

but we could not do something like

var firstFragmentIsWhitespace =
  (content[0].CharacterCategorisation === parser.CharacterCategorisationOptions.Whitespace);

since the "CharacterCategorisationOptions" type is not exported from the module.

Using the "declare module" approach allows us to nest the enum in that module which then is exported and then so can be accessed by the calling code.

The same applies to exporting nested classes. Which leads me on to the next part of the parser interface - if the parsing method encounters content that it can not parse then it will throw a "ParseError". This error class has "name" and "message" properties like any other JavaScript Error but it has an additional "indexInSource" property to indicate where the troublesome character(s) occurred.

The type definition now looks like

declare module CssParser {
  export function ParseCss(content: string): CategorisedCharacterString[];

  export interface CategorisedCharacterString {
    Value: string;
    IndexInSource: number;
    CharacterCategorisation: CharacterCategorisationOptions;
  }

  export const enum CharacterCategorisationOptions {
    Comment = 0,
    CloseBrace = 1,
    OpenBrace = 2,
    SemiColon = 3,
    SelectorOrStyleProperty = 4,
    StylePropertyColon = 5,
    Value = 6,
    Whitespace = 7
  }

  export class ParseError implements Error {
    constructor(message: string, indexInSource: number);
    name: string;
    message: string;
    indexInSource: number;
  }      
}

export = CssParser;

There are complications around extending the Error object in both JavaScript and TypeScript, but I don't need to worry about that here since the library deals with it, all I need to do is describe the library's interface.

This type definition now supports the following consuming code -

import parser = require("cssparser/CssParserJs");

try {
  var content = parser.ParseCss("body { color: red; }");
  console.log("Parsed into " + content.length + " segment(s)");
}
catch (e) {
  if (e instanceof parser.ParseError) {
    var parseError = <parser.ParseError>e;
    console.log("ParseError at index " + parseError.indexInSource + ": " + parseError.message);
  }
  else {
    console.log(e.message);
  }
}

The library has two other methods to expose yet. As well as "ParseCss" there is a "ParseLess" function - this applies slightly different rules, largely around the handling of comments (Less supports single line comments that start with "//" whereas CSS only allows the "/* .. */" format).

And then there is the "ExtendedLessParser.ParseIntoStructuredData" method. "ParseCss" and "ParseLess" do a very cheap pass through style content to try to break it down and categorise sections while "ParseIntoStructuredData" takes that data, processes it more thoroughly and returns a hierarchical representation of the styles.

The final type definition becomes

declare module CssParser {
  export function ParseCss(content: string): CategorisedCharacterString[];
  export function ParseLess(content: string): CategorisedCharacterString[];

  export module ExtendedLessParser {
    export function ParseIntoStructuredData(
      content: string | CategorisedCharacterString[],
      optionallyExcludeComments?: boolean
    ): CssFragment[];

    interface CssFragment {
      FragmentCategorisation: FragmentCategorisationOptions;
      Selectors: string[];
      ParentSelectors: string[][];
      ChildFragments: CssFragment;
      SourceLineIndex: number;
    }
    export const enum FragmentCategorisationOptions {
      Comment = 0,
      Import = 1,
      MediaQuery = 2,
      Selector = 3,
      StylePropertyName = 4,
      StylePropertyValue = 5
    }
  }

  export interface CategorisedCharacterString {
    Value: string;
    IndexInSource: number;
    CharacterCategorisation: CharacterCategorisationOptions;
  }
  export const enum CharacterCategorisationOptions {
    Comment = 0,
    CloseBrace = 1,
    OpenBrace = 2,
    SemiColon = 3,
    SelectorOrStyleProperty = 4,
    StylePropertyColon = 5,
    Value = 6,
    Whitespace = 7
  }

  export class ParseError implements Error {
    constructor(message: string, indexInSource: number);
    name: string;
    message: string;
    indexInSource: number;
  }
}

export = CssParser;

The "ExtendedLessParser.ParseIntoStructuredData" nested method is exposed as a function within a nested module. Similarly, the interface and enum for its return type are both nested in there. The method signature is somewhat interesting in that the library will accept either a string being passed into "ParseIntoStructuredData" or the result of a "ParseLess" call. TypeScript has support for this and the method signature indicates that it will accept either "string" or "CategorisedCharacterString[]" (this relies upon "union type" support that became available in TypeScript 1.4). There is also an optional argument to indicate that comments should be excluded from the return content, this is also easy to express in TypeScript (by including the question mark after the argument name).

Limitations

For the module at hand, this covers everything that I needed to do!

However.. while reading up further on type definitions, I did come across one limitation that I think is unfortunate. There is no support for get-only properties on either interfaces or classes. For my CSS Parser, that isn't an issue because I didn't write it in a manner that enforced immutability. But if the CssFragment type (for example) was written with properties that only supported "get" then I might have wanted to write the interface as

interface CssFragment {
  get FragmentCategorisation(): FragmentCategorisationOptions;
  get Selectors(): string[];
  get ParentSelectors(): string[][];
  get ChildFragments(): CssFragment;
  get SourceLineIndex(): number;
}

But this is not supported. You will get compile errors.

In fairness, this shouldn't be a surprise, since TypeScript does not support properties in interfaces in its regular code; so it's not only within type definitions that it throws its toys out of the pram when you try to do this.

So, instead, you might try to represent that data with a class, since classes do support get-only properties in regular TypeScript. However, if you attempt to write

export class CssFragment {
  get FragmentCategorisation(): FragmentCategorisationOptions;
  get Selectors(): string[];
  get ParentSelectors(): string[][];
  get ChildFragments(): CssFragment;
  get SourceLineIndex(): number;
}

then you would still receive a compile error

An accessor cannot be declared in an ambient context

Interestingly, this should also not be too surprising (although it surprised me until I looked into it!) since the following code is legal:

class ClassWithGetOnlyName {
  get name(): string {
    return "Jim";
  }
}

var example = new ClassWithGetOnlyName();
example.name = "Bob"; // Setting a property that only has a getter!

alert(example.name);

Here, the alert will show "Jim" since that is what the property getter returns. But it is not illegal to try to set the property (it's just that the "setting" action is effectively ignored). So TypeScript doesn't support the notion of a "get-only" (or "readonly") property.

I think this is unfortunate, considering there are more and more libraries being released that incorporate immutability (Facebook released a library dedicated to immutable collections: immutable-js). There are issues in TypeScript's GitHub repo about this already, albeit with no ready solution available: see Compiler allows assignments to read-only properties and Suggestion: read-only modifier.

If you're writing a library from scratch that has immutable types then you can work around it by returning data from functions instead of properties - eg.

class ClassWithGetOnlyName {
  getName(): string {
    return "Jim";
  }
}

var example = new ClassWithGetOnlyName();
alert(example.getName());

However, if you wanted to write a type definition for an existing library that was intended to return immutable types (that exposed the data through properties) then you would be unable to represent this in TypeScript. Which is a pity.

Which leaves me ending on a bum note when, otherwise, this exercise has been a success! So let's forget the downsides for now and celebrate the achievements instead! The CSS Parser JavaScript port is now available with a TypeScript definition file - hurrah! Everyone should now scurry off and download it from either npm (npmjs.com/package/cssparserjs) or bitbucket.org/DanRoberts/cssparserjs and get parsing!! :)

Posted at 19:19

Comments

TypeScript / ES6 classes for React components - without the hacks!

React 0.13 has just been released into beta, a release I've been eagerly anticipating! This has been the release where finally they will be supporting ES6 classes to create React components. Fully supported, no messing about and jumping through hoops and hoping that breaking API changes don't drop in and catch you off guard.

Back in September, I wrote about Writing React components in TypeScript and realised that before I had actually posted it that the version of React I was using was out of date and I would have to re-work it all again or wait until ES6 classes were natively supported (which was on the horizon back then, it's just that there were no firm dates). I took the lazy option and have been sticking to React 0.10.. until now!

Update (16th March 2015): React 0.13 was officially released last week, it's no longer in beta - this is excellent news! There appear to be very little changed since the beta so everything here is still applicable.

Getting the new code

I've got my head around npm, which is the recommended way to get the source. I had a few teething problems a few months ago with first getting going (I need python?? Oh, not that version..) but now everything's rosy. So off I went:

npm install react@0.13.0-beta.1

I saw that the "lib" folder had the source code for the files, the dependencies are all nicely broken up. Then I had a small meltdown and stressed about how to build from source - did I need to run browserify or something?? I got that working, with some light hacking it about, and got to playing around with it. It was only later that I realised that there's also a "dist" folder with built versions - both production (ie. minified) and development. Silly boy.

To start with, I stuck to vanilla JavaScript to play around with it (I didn't want to start getting confused as to whether any problems were with React or with TypeScript with React). The online JSX Compiler can perform ES6 translations as well as JSX, which meant that I could take the example

class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}

React.render(<HelloMessage name="Sebastian" />, mountNode);

and translate it into JavaScript (this deals with creating a class, derives it "from React.Component" and it illustrates what the JSX syntax hides - particularly the "React.createElement" call):

var ____Class1 = React.Component;
for (var ____Class1____Key in ____Class1) {
  if (____Class1.hasOwnProperty(____Class1____Key)) {
    HelloMessage[____Class1____Key] = ____Class1[____Class1____Key];
  }
}
var ____SuperProtoOf____Class1 = ____Class1 === null ? null : ____Class1.prototype;
HelloMessage.prototype = Object.create(____SuperProtoOf____Class1);
HelloMessage.prototype.constructor = HelloMessage;
HelloMessage.__superConstructor__ = ____Class1;

function HelloMessage() {
  "use strict";
  if (____Class1 !== null) {
    ____Class1.apply(this, arguments);
  }
}
HelloMessage.prototype.render = function() {
  "use strict";
  return React.createElement("div", null, "Hello ", this.props.name);
};

React.render(
  React.createElement(HelloMessage, { name: "Sebastian" }),
  mountNode
);

I put this into a test page and it worked! ("mountNode" just needs to be a container element - any div that you want to render your content inside).

The derive-class code isn't identical to that you see in TypeScript's output. If you've looked at what TypeScript emits, this might be familiar:

var __extends = this.__extends || function (d, b) {
  for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  function __() { this.constructor = d; }
  __.prototype = b.prototype;
  d.prototype = new __();
};

I tried hacking this in, in place of the inheritance approach from the JSX Compiler and it still worked. I presumed it would, but it's always best to take baby steps if you don't understand it all perfectly - and I must admit that I've been a bit hazy on some of React's terminology around components, classes, elements, factories, whatever.. (despite having read "Introducing React Elements" what feels like a hundred times).

Another wrong turn

In the code above, the arrangement of the line

React.render(
  React.createElement(HelloMessage, { name: "Sebastian" }),
  mountNode
);

is very important. I must have spent hours earlier struggling with getting it working in TypeScript because I thought it was

React.render(
  React.createElement(new HelloMessage({ name: "Sebastian" })),
  mountNode
);

It's not.

It it not a new instance passed to "createElement"; it's a type and a properties object. I'm not sure where I got the idea from that it was the other way around - perhaps because I got all excited about it working with classes and then presumed that you worked directly with instances of those classes. Doh.

Time for TypeScript!

Like I said, I've been clinging to my hacked-about way to get TypeScript working with React until now (waiting until I could throw it away entirely, rather than replace it for something else.. which I would then have to throw away entirely when this release turned up). I took a lot of inspiration from code in the react-typescript repo. But that repo hasn't been kept up to date (for the same reason as I had, I believe, that the author knew that it was only going to be required until ES6 classes were supported). There is a link there to typed-react, which seems to have been maintained for 0.12. This seemed like the best place to start.

Update (16th March 2015): With React 0.13's official release, the DefinitelyTyped repo has been updated and now does work with 0.13, I'm leaving the below section untouched for posterity but you might want to skip to the next section "Writing a TypeScript React component" if you're using the DefinitelyTyped definition.

In fact, after some investigation, very little needs changing. Starting with their React type definitions (from the file typings/react/reactd.ts), we need to expose the "React.Component" class but currently that is described by an interface. So the following must be changed -

interface Component<P> {
  getDOMNode<TElement extends Element>(): TElement;
  getDOMNode(): Element;
  isMounted(): boolean;
  props: P;
  setProps(nextProps: P, callback?: () => void): void;
  replaceProps(nextProps: P, callback?: () => void): void;
}

for this -

export class Component<P> {
  constructor(props: P);
  protected props: P;
}

I've removed isMounted and setProps because they've been deprecated from React. I've also removed the getDOMNode methods since I think they spill out more information than is necessary and I've removed replaceProps since the way that I've been using React I've not seen the use for it - I think it makes more sense to request a full re-render* rather than poke things around. You may not agree with me on these, so feel free to leave them in! Similarly, I've changed the access level of "props" to protected, since I don't think that it should be public information. This requires TypeScript 1.3, which might be why the typed-react version doesn't specify it.

* When I say "re-render", I mean that when some action changes the state of the application, I call React.render again and let the Virtual DOM do it's magic around making this efficient. Plus I'm experimenting at the moment with making the most of immutable data structures and returning false from shouldComponentUpdate where it's clear that the data can't have changed - so the Virtual DOM has less work to do. But that's straying from the point of this post a bit..

Then the external interface needs changing from

interface Exports extends TopLevelAPI {
  DOM: ReactDOM;
  PropTypes: ReactPropTypes;
  Children: ReactChildren;
}

to

interface Exports extends TopLevelAPI {
  DOM: ReactDOM;
  PropTypes: ReactPropTypes;
  Children: ReactChildren;
  Component: Component<any>;
}

Quite frankly, I'm not 100% sure why specifying "Component" works as it does, since I would have thought that you could only then inherit from "Component", rather than being able to specify whatever type param that you want. But it does work, thankfully (my understanding of type definitions is a little shallow at this point, so there's very likely something here that I don't quite understand which allows it work as it does).

Writing a TypeScript React component

So now we can write this:

import React = require('react');

interface Props { name: string; role: string; }

class PersonDetailsComponent extends React.Component<Props> {
  constructor(props: Props) {
    super(props);
  }
  public render() {
    return React.DOM.div(null, this.props.name + " is a " + this.props.role);

  }
}

function Factory(props: Props) {
  return React.createElement(PersonDetailsComponent, props);
}

export = Factory;

Note that we are able to specify a type param for "React.Component" and, when you edit this in TypeScript, "this.props" is correctly identified as being of that type.

Update (16th March 2015): If you are using the DefinitelyTyped definitions then you need to specify both "Props" and "State" type params (I recommend that Component State never be used and that it always be specified as "{}", but that's out of the scope of this post) - ie.

class PersonDetailsComponent extends React.Component<Props, {}> {

The pattern I've used is to declare a class that is not exported. Rather, a "Factory" function is made available to the world. This is to prevent the problem that I described earlier - originally I was exporting the class and was trying to call

React.render(
  React.createElement(new PersonDetailsComponent({
    name: "Bob",
    role: "Mouse catcher"
  })),
  mountNode
);

but this does not work. The correct approach is to export a Factory method and then to consume the component thusly:

React.render(
  PersonDetailsComponent({
    name: "Bob",
    role: "Mouse catcher"
  }),
  this._renderContainer
);

Thankfully, the render method is specified in the type definition as

render<P>(
  element: ReactElement<P>,
  container: Element,
  callback?: () => void
): Component<P>;

so, if you forget to apply the structure of non-exported-class / exported-Factory-method and tried to export the class and new-one-up and pass it to "React.render" directly, you would get a compile error such as

Argument of type 'PersonDetailsComponent' is not assignable to parameter of type 'ReactElement<Props>'

I do love it when the compiler can pick up on your silly mistakes!

Update (16th March 2015): Again, there is a slight difference between the typed-react definition that I was originally using and the now-updated DefinitelyTyped repo version. With DefinitelyTyped, the render method is specified as:

render<P, S>(
    element: ReactElement<P>,
    container: Element,
    callback?: () => any
): Component<P, S>

but the meaning is much the same.

Migration plan

The hacky way I've been working until now did allow instances of component classes to be used, so migrating over is going to require some boring mechanical work to change them - and to add Factory methods to each component. But, since they all shared a common base class (the "ReactComponentBridge"), it also shouldn't be too much work to change those base classes to "React.Component" in one search-and-replace. And there aren't too many other breaking changes to worry about. I was using "setProps" earlier on in development but I've already gotten rid of all those - so I'm optimistic that moving over to 0.13 isn't going to be too big of a deal.

It's worth bearing in mind that 0.13 is still in beta at the moment, but it seems like the changes that I'm interested in here are unlikely to vary too much between now and the official release. So if I get cracking, maybe I can finish migrating not long after it's officially here - instead of being stuck a few releases behind!

Posted at 01:02

Comments

TypeScript classes for (React) Flux actions

I've been playing with React over the last few months and I'm still a fan. I've followed Facebook's advice and gone with the "Flux" architecture (there's so many good articles about this out there that I couldn't even decide whic one to link to) but I've been writing the code using TypeScript. So far, most of my qualms with this approach have been with TypeScript rather than React; I don't like the closing-brace formatting that Visual Studio does and doesn't let you change, its generics system is really good but not quite as good as I'd like (not as good as C#'s, for example, and I sometimes wish generic type params were available at runtime for testing but I do understand why they're not). I wish the "Allow implicit 'any' types" option defaulted to unchecked rather than checked (I presume this is to encourage "gradual typing" but if I'm using TypeScript I'd rather go whole-hog).

But what I thought were going to be the big problems with it haven't been, really - type definitions and writing the components (though I am using a bit of a hack that relies upon an older version of React - I'm hoping to change this when 0.13 comes out and introduces better support for ES6 classes).

Writing the components in "pure" TypeScript results in more code than jsx.. it's not the end of the world, but something that would combine the benefits of both (strong typing and succint jsx format) would be wonderful. There are various possibilities that I believe people are looking into, from modifying the TypeScript compiler to support jsx to the work that Facebook themselves are doing around "Flow" which "Adds static typing to JavaScript to improve developer productivity and code quality". Neither of these are ready for me to integrate into Visual Studio, which I'm still using since I like it so much for my other development work.

What I want to talk about today, though, is one of the ways that TypeScript's capabilities can make a nice tweak to how the Flux architecture may be realised. Hopefully the following isn't blindly obvious and well-known, I failed to find any other posts out there explaining it so I'm going to try to take credit for it! :)

As recommended and apparently done by everyone..

Here's the diagram that everyone who's looked into Flux will have seen many times before (since I've nicked it straight from the React blog's post about it) -

The Flux Architecture

In the middle are the "Action Creators", which create objects that represent actions (and any associated data) so that the Dispatcher has something to send out. Stores listen for these actions - checking whether a given action is one that they're interested in and extracting the information from it as required.

As a concrete example, here is how actions are created in Facebook's "TODO" example (from their repo on GitHub):

/*
 * Copyright (c) 2014, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * TodoActions
 */

var AppDispatcher = require('../dispatcher/AppDispatcher');
var TodoConstants = require('../constants/TodoConstants');

var TodoActions = {

  /**
   * @param  {string} text
   */
  create: function(text) {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_CREATE,
      text: text
    });
  },

  /**
   * @param  {string} id The ID of the ToDo item
   * @param  {string} text
   */
  updateText: function(id, text) {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_UPDATE_TEXT,
      id: id,
      text: text
    });
  },

  /**
   * Toggle whether a single ToDo is complete
   * @param  {object} todo
   */
  toggleComplete: function(todo) {
    var id = todo.id;
    if (todo.complete) {
      AppDispatcher.dispatch({
        actionType: TodoConstants.TODO_UNDO_COMPLETE,
        id: id
      });
    } else {
      AppDispatcher.dispatch({
        actionType: TodoConstants.TODO_COMPLETE,
        id: id
      });
    }
  },

  /**
   * Mark all ToDos as complete
   */
  toggleCompleteAll: function() {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_TOGGLE_COMPLETE_ALL
    });
  },

  /**
   * @param  {string} id
   */
  destroy: function(id) {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_DESTROY,
      id: id
    });
  },

  /**
   * Delete all the completed ToDos
   */
  destroyCompleted: function() {
    AppDispatcher.dispatch({
      actionType: TodoConstants.TODO_DESTROY_COMPLETED
    });
  }

};

module.exports = TodoActions;

Every action has an "actionType" property. Some have an "id" property, some have a "text" property, some have both, some have neither. Other examples I've seen follow a similar pattern where the ActionCreator (or ActionCreators, since sometimes there are multiple - as in the chat example in that same Facebook repo) is what is responsible for knowing how data is represented by each action. Stores assume that if the "actionType" is what they expect then all of the other properties they expect to be associated with that action will be present.

Here's a snippet I've taken from another post:

var action = payload.action;
switch(action.actionType){
  case AppConstants.ADD_ITEM:
    _addItem(payload.action.item);
    break;
  case AppConstants.REMOVE_ITEM:
    _removeItem(payload.action.index);
    break;
  case AppConstants.INCREASE_ITEM:
    _increaseItem(payload.action.index);
    break;
  case AppConstants.DECREASE_ITEM:
    _decreaseItem(payload.action.index);
    break;
}

Some actions have an "item" property, some have an "index". The ActionCreator was responsible for correctly populating data appropriate to the "actionType".

Types, types, types

When I first start writing code like this for my own projects, it felt wrong. Wasn't I using TypeScript so that I had a nice reassuring type safety net to protect me against my own mistakes?

Side note: For me, this is one of the best advantages of "strong typing", the fact the compiler can tell me if I've mistyped a property or argument, or if I want to change the name of one of them then the compiler can change all references rather than it being a manual process. The other biggie for me is how beneficial it can be in helping document APIs (both internal and external) - for other people using my code.. or just me when it's been long enough that I can't remember all of the ins and outs of what I've written! These are more important to me than getting worried about whether "static languages" can definitely perform better than "dynamic" ones (let's not open that can of worms).

Surely, I asked myself, if these objects have properties that vary based upon an "actionType" magic string, these would be better expressed as actual types? Like classes?

Working from the example above, there would be classes such as:

class AddItemAction {
  constructor(private _index: number) { }
  get index() {
    return this._index;
  }
}
export = AddItemAction;

I'm a fan of the AMD pattern so I would have a separate file per action class and then explicitly "import" (in TypeScript terms) them into Stores that reference them. The main reason I'm leaning towards the AMD pattern is that you can use require.js to load in the script required to render the first "page" and then dynamically load in additional script as more functionality of the application is used. This should avoid the risk of the dreaded multi-megabyte initial download (and the associated delays). I'm still proving this to myself - it's looking very promising so far but I haven't written any multi-megabyte applications yet!

I also like things to be immutable, otherwise the above could have been shortened even further to:

class AddItemAction {
  constructor(public index: number) { }
}
export = AddItemAction;

But, technically, this could lead to one Store changing data in an action, which could affect what another Store does with the data. An effect that would only happen if that first Store received the action before the second one. Yuck. I don't imagine anyone would want to do something like that but immutability means that it's not even possible, even by accident (especially by accident).

So if there were classes for each action then the listening code would look more like this:

if (action instanceof AddItemAction) {
  this._addItem(action);
}
if (action instanceof RemoveItemAction) {
  this._removeItem(action);
}
if (action instanceof IncreaseItemAction) {
  this._increaseItem(action);
}
if (action instanceof DecreaseItemAction) {
  this._decreaseItem(action);
}

I prefer to have the functions receive the actual action. The AddItemAction instance is passed to the "_addItem" function, for example, rather than just the "index" property value - eg.

private _addItem(action: AddItemAction) {
  // Do whatever..
}

This is at least partly because it makes the type comparing code more succinct - the "action" reference will be of type "any" (as will be seen further on in this post) and so TypeScript lets us pass it straight in to methods such as _addItem since it presumes that if it's "any" then it can be used anywhere, even as an function argument that has a specific type annotation. The type check that is made before _addItem is called gives us the confidence that the data is appropriate to pass to _addItem, the TypeScript compiler will then happily take our word for it.

Update (25th February 2015): A couple of people in the comments suggested that the action property on the payload should implement an interface to "mark" it as an action. This is something I considered originally but I dismissed it and I think I'm going to continue to dismiss it for the following reason: the interface would be "empty" since there is no property or method that all actions would need to share. If this were C# then every action class would have to explicitly implement this "empty interface" and so we could do things like search for all implementation of IAction within a given project or binary. In TypeScript, however, interfaces may be implemented implicitly ("TypeScript is structural"). This means that any object may be considered to have (implicitly) implemented IAction, if IAction is an empty interface. And this means that there would be no reliable way to search for implementations of IAction in a code base. You could search for classes that explicitly implement it, but if you have to rely upon people to follow the convention of decorating all action classes with a particular interface then you might as well rely on a simpler convention such as keeping all actions within files under an "action" folder.

Server vs User actions

Another concept that this works well with is one that I think I first read at Atlassian's blog: Flux Step By Step - the idea of identifying a given action as originating from a view (from a user interaction, generally) or from the server (such as an ajax callback).

They suggested the use of an AppDispatcher with two distinct methods, each wrapping an action up with an appropriate "source" value -

var AppDispatcher = copyProperties(new Dispatcher(), {

  /**
   * @param {object} action The details of the action, including the action's
   * type and additional data coming from the server.
   */
  handleServerAction: function(action) {
    var payload = {
      source: 'SERVER_ACTION',
      action: action
    };
    this.dispatch(payload);
  },

  /**
   * @param {object} action The details of the action, including the action's
   * type and additional data coming from the view.
   */
  handleViewAction: function(action) {
    var payload = {
      source: 'VIEW_ACTION',
      action: action
    };
    this.dispatch(payload);
  }

});

Again, these are "magic string" values. I like the idea, but TypeScript has the tools to do better.

I have a module with an enum for this:

enum PayloadSources {
  Server,
  View
}
export = PayloadSources;

and then an AppDispatcher of my own -

import Dispatcher = require('third_party/Dispatcher/Dispatcher');
import PayloadSources = require('constants/PayloadSources');
import IDispatcherMessage = require('dispatcher/IDispatcherMessage');

var appDispatcher = (function () {
  var _dispatcher = new Dispatcher();
  return {
    handleServerAction: function (action: any): void {
      _dispatcher.dispatch({
        source: PayloadSources.Server,
        action: action
      });
    },

    handleViewAction: function (action: any): void {
      _dispatcher.dispatch({
        source: PayloadSources.View,
        action: action
      });
    },

    register: function (callback: (message: IDispatcherMessage) => void): string {
      return _dispatcher.register(callback);
    },

    unregister: function (id: string): void {
      return _dispatcher.unregister(id);
    },

    waitFor: function (ids: string[]): void {
      _dispatcher.waitFor(ids);
    }
  };
} ());

// This is effectively a singleton reference, as seems to be the standard pattern for Flux
export = appDispatcher;

The IDispatcherMessage is very simple:

import PayloadSources = require('constants/PayloadSources');
interface IDispatcherMessage {
  source: PayloadSources;
  action: any
}
export = IDispatcherMessage;

This allows me to listen for actions with code thusly -

AppDispatcher.register(message => {
  var action = message.action;
  if (action instanceof AddItemAction) {
    this._addItem(action);
  }
  if (action instanceof RemoveItemAction) {
    this._removeItem(action);
  }
  // etc..

Now, if I come across a good reason to rename the "index" property on the AddItemAction class, I can perform a refactor action that will fix it everywhere. If I don't use the IDE to perform the refactor, and just change the property name in one place, then I'll get TypeScript compiler errors about an "index" property that no longer exists.

The mysterious Dispatcher

One thing I skimmed over in the above is what the "third_party/Dispatcher/Dispatcher" component is. The simple answer is that I took the Dispatcher.js file from the Flux repo and messed about with it a tiny bit to get it to compile as TypeScript with my preferred disabling of the option "Allow implicit 'any' types". In case this is a helpful place for anyone to start, I've put the result up on pastebin as TypeScript Flux Dispatcher, along with the required support class TypeScript Flux Dispatcher - invariant support class.

Final notes

I'm still experimenting with React and Flux but this is one of the areas that I've definitely been happy with. I like the Flux architecture and the very clear way in which interactions are handled (and the clear direction of flow of information). Describing the actions with TypeScript classes feels very natural to me. It might be that I start grouping multiple actions into a single module as my applications get bigger, but for now I'm fine with one per file.

The only thing I'm only mostly happy with is my bold declaration in the AppDispatcher class; "This is effectively a singleton reference, as seems to be the standard pattern for Flux". It's not the class that's exported from that module, it's an instance of the AppDispatcher which is used by everything in the app. This makes sense in a lot of ways, since it needs to be used in so many places; there will be various Stores that register to listen to it but there are likely to be many, many React components, any one of which could accept some sort of interaction that requires an action be created (and so be sent to the AppDispatcher). One alternative approach would be to use dependency injection to pass an AppDispatcher through every component that might need it. In fact, I did try that in one early experiment but found it extremely cumbersome, so I'm happy to settle for what I've got here.

However, the reason (one of, at least!) that singletons got such a bad name is that they can making unit testing very awkward. I'm still in the early phases of investigating what I think is the best way to test a React / Flux application (there are a lot of articles out there explaining good ways to tackle it and I'm trying to work my way through some of their ideas). One thing that I'm contemplating, particularly for testing simple React components, is to take advantage of the fact that I'm using AMD everywhere and to try changing the require.js configuration for tests - for any given test, when an AppDispatcher is requested, some sort of mock object could be provided in its place.

This would have the two main benefits that it could expose convenient methods to confirm that a particular action was raised following a given interaction (which may be the main point of that particular test) but also that there would be no shared state that needs resetting between tests; each test would provide its own AppDispatcher stand-in. I've not properly explored this yet, it's still in the idea phase, but I think it also has promise. And - if it all goes to plan - it's another reason way for me to convince myself that AMD loading within TypeScript is the way to go!

Posted at 22:24

Comments

Writing React components in TypeScript

Whoops.. I started writing this post a while ago and have only just got round to finishing it off. Now I realise that it applies to React 0.10 but the code here doesn't work in the now-current 0.11. On top of this, hopefully this will become unnecessary when 0.12 is released. I talk about this in the last part of the post. But until 0.12 is out (and confirmed to address the problem), I'm going to stick to 0.10 and use the solution that I talk about here.

Update (29th January 2015): React 0.13 beta has been released and none of this workaround is required any more - I've written about it here: TypeScript / ES6 classes for React components - without the hacks!

I've been playing around with React recently, putting together some prototypes to try to identify any pitfalls in what I think is an excellent idea and framework, with a view to convincing everyone else at work that we should consider it for new products. I'm no JavaScript hater but I do strongly believe in strongly typed code being easier to maintain in the long run for projects of significant size. Let's not get into an argument about whether strong or "weak" typing is best - before we know it we could end up worrying about what strongly typed even means! If you don't agree with me then you probably don't see any merit to TypeScript and you probably already guessed that this post will not be of interest to you! :)

So I wanted to try bringing together the benefits of React with the benefits of TypeScript.. I'm clearly not the only one since there is already a type definition available in NuGet: React.TypeScript.DefinitelyTyped. This seems to be the recommended definition and appears to be in active development. I'd love it even more if there was an official definition from Facebook themselves (they have one for their immutable-js library) but having one here is a great start. This allows us to call methods in the React library and know what types the arguments should be and what they will return (and the compiler will tell us if we break these contracts by passing the wrong types or trying to mistreat the return values).

However, there are a few problems. Allow me to venture briefly back to square one..

Back to basics: A React component

This is a very simple component in React -

var MyButton = React.createClass({
  _clickHandler: function() {
    alert('Clicked MyButton with message "' + this.props.message + '"');
  },
  render: function() {
    return <button onClick={this._clickHandler}>{this.props.message}</button>;
  }
});

It's pretty boring but it illustrates a few principles. Firstly, it's written in "jsx" - a format like JavaScript but that needs some processing to actually become JavaScript. The <button> declaration looks like html, for example, and needs altering to become real JavaScript. If we're going to write components in TypeScript then we can't use this format since Visual Studio doesn't understand it (granted I'm making a bit of a leap assuming that you're using Visual Studio for this - it's not necessary, but I suspect most people writing TypeScript will use it since the TypeScript support is so good).

The good news is that the translation from "jsx" to JavaScript is not a complex one*. It results in slightly longer code but it's still easily readable (and writable). So the above would be, written in native JavaScript -

var MyButton = React.createClass({
  _clickHandler: function() {
    alert('Clicked MyButton with message "' + this.props.message + '"');
  },
  render: function() {
    return React.DOM.button({ onClick: this._clickHandler }, this.props.message);
  }
});

* (It can do other clever stuff like translate "fat arrow" functions into JavaScript that is compatible with older browsers, but let's not get bogged down with that here - since I want to use TypeScript rather than jsx, it's not that relevant right now).

This simple example is illustrating something useful that can be taken for granted since React 0.4; autobinding. When "_clickHandler" is called, the "this" reference is bound to the component instance, so "this.props.message" is accessible. Before 0.4, you had to use the "React.autoBind" method - eg.

var MyButton = React.createClass({
  _clickHandler: React.autoBind(function() {
    alert('Clicked MyButton with message "' + this.props.message + '"');
  }),
  render: function() {
    return React.DOM.button({ onClick: this._clickHandler }, this.props.message);
  }
});

but these days it just works as you would expect (or as you would hope, perhaps). This happened back in July 2013 - see New in React v0.4: Autobind by Default.

A TypeScript React component: Take 1

If we naively try to write TypeScript code that starts off with the JavaScript above then we find we have no intellisense. The editor has no idea about "this.props" - no idea that it is defined, certainly no idea that it has a property "message" that should be a string. This shouldn't really be a surprise since the "this" in this case is just an anonymous object that we're passing to "React.createClass"; no information about the type has been specified, so it is considered to be of type "any".

TypeScript React Component 'this' issue

If we continue like this then we're going to miss out on the prime driver for using TypeScript in the first place - we might as well just write the components in JavaScript or "jsx"! (In fairness, this is something that I considered.. with React, and particularly the recommended Flux architecture, the "view components" are a relatively thin layer over components that could easily be written in TypeScript and so benefit from being strongly typed.. the view components could remain "more dynamic" and be covered by the class of unit tests that are often used to cover cases that are impossible with the guarantees of strong typing).

The obvious thing to try was to have a TypeScript class along the lines of

class MyButton {
  props: { message: string };
  private _clickHandler() {
    alert('Clicked MyButton with message "' + this.props.message + '"');
  }
  public render() {
    return React.DOM.button({ onClick: this._clickHandler }, this.props.message);
  }
}

var MyButtonReactComponent = React.createClass(new MyButton());

This would solve the internal type specification issue (where "this" is "any"). However, when the "React.createClass" function is called at runtime, an error is thrown..

Error: Invariant Violation: createClass(...): Class specification must implement a render method.

I'm not completely sure, but I suspect that the React framework code is expecting an object with a property that is a function named "render" while the class instance passed to it has a function "render" on its prototype rather than a property on the reference itself.

Looking for help elsewhere

When I got to this point, I figured that someone else must have had encountered the same problem - particularly since there exists this TypeScript definition for React in the first place! I came across a GitHub project React TypeScript which describes itself as a

React wrapper to make it play nicely with typescript.

An example in the README shows

import React = require('react');
import ReactTypescript = require('react-typescript');

class HelloMessage extends ReactTypescript.ReactComponentBase<{ name: string; }, {}> {
  render() {
    return React.DOM.div(null, 'Hello ' + this.props.name);
  }
}

React.renderComponent(new HelloMessage({ name: 'Jhon' }), mountNode);

which looks like exactly what I want!

The problems are that it clearly states..

warning: ReactTypescript can actually only be used with commonjs modules and browserify, if someone does want AMD I'll gladly accept any PR that would packages it for another format.

.. and I'm very interesting in using AMD and require.js to load modules "on demand" (so that if I develop a large app then I have a way to prevent the "megabyte-plus initial JavaScript download").

Also, I'm concerned that the maintained TypeScript definition that I referenced earlier claims to be

Based on TodoMVC sample by @fdecampredon, improved by @wizzard0, MIT licensed.

fdecampredon is the author of this "React TypeScript" repo.. which hasn't been updated in seven months. So I'm concerned that the definitions might not be getting updated here - there are already a lot of differences between the react.d.ts in this project and that in the maintained NuGet package's react.d.ts.

In addition to this, the README states that

In react, methods are automatically bound to a component, this is not the case when using ReactTypeScript, to activate this behavious you can use the autoBindMethods function of ReactTypeScript

This refers to what I talked about earlier; the "auto-binding" convenience to make writing components more natural. There are two examples of ways around this. You can use the ReactTypeScript library's "autoBindMethods" function -

class MyButton extends ReactTypeScript.ReactComponentBase<{ message: string}, any> {
  clickHandler(event: React.MouseEvent) {
    alert(this.props.message);
  }
  render() {
    return React.DOM.button({ onClick: this.clickHandler }, 'Click Me');
  }
}

// If this isn't called then "this.props.message" will error in clickHandler as "this" is not
// bound to the instance of the class
ReactTypeScript.autoBindMethods(MyButton);

or you can use the TypeScript "fat arrow" to bind the function to the "this" reference that you would expect:

class MyButton extends  ReactTypeScript.ReactComponentBase<{ message: string}, any> {
  // If the fat arrow isn't used for the clickHandler definition then "this.props.message" will
  // error in clickHandler as "this" is not bound to the instance of the class
  clickHandler = (event: React.MouseEvent) => {
    alert(this.props.message);
  }
  render() {
    return React.DOM.button({ onClick: this.clickHandler }, 'Click Me');
  }
}

The first approach feels a bit clumsy, that you must always remember to call this method for all component classes. The second approach doesn't feel too bad, it's just a case of being vigilant and always using fat arrows - but if you forget, you won't find out until runtime. Considering that I want to use to TypeScript to catch more errors at compile time, this still doesn't feel ideal.

The final concern I have is that the library includes a large-ish react-internal.js file. What I'm going to suggest further down does unfortunately dip its toe into React's (undocumented) internals but I've tried to keep it to the bare minimum. This "react-internal.js" worries me as it might be relying on a range of implementation details, any of which (as far as I know) could potentially change and break my code.

In case I'm sounding down on this library, I don't mean to be - I've tried it out and it does actually work, and there are not a lot of successful alternatives out there. So I've got plenty of respect for this guy, getting his hands dirty and inspiring me to follow in his footsteps!

Stealing Taking inspiration - A TypeScript React component: Take 2

So I want a way to

  1. Write a TypeScript class that can be used as a React component
  2. Use the seemingly-maintained NuGet-delivered type definition and limit access to the "internals" as much as possible
  3. Have the component's methods always be auto-bound

I'd better say this up-front, though: I'm willing to sacrifice the support for mixins here.

fdecampredon's "React TypeScript" library does support mixins so it's technically possible but I'm not convinced at this time that they're worth the complexity required by the implementation since I don't think they fit well with the model of a TypeScript component.

The basic premise is that you can name mixin objects which are "merged into" the component code, adding properties such as functions that may be called by the component's code. Since TypeScript wouldn't be aware of the properties added by mixins, it would think that there were missing methods / properties and flag them as errors if they were used within the component.

On top of this, I've not been convinced by the use cases for mixins that I've seen so far. In the official React docs section about mixins, it uses the example of a timer that is automatically cleared when the component is unmounted. There's a question on Stack Overflow "Using mixins vs components for code reuse in Facebook React" where the top answer talks about using mixins to perform common form validation work to display errors or enable or disable inputs by directly altering internal state on the component. As I understand the Flux architecture, the one-way message passing should result in validation being done in the store rather than the view / component. This allows the validation to exist in a central (and easily-testable) location and to not exist in the components. This also goes for the timer example, the logic-handling around whatever events are being raised on a timer should not exist within the components.

What I have ended up with is this:

import React = require('react');

// The props and state references may be passed in through the constructor
export class ReactComponentBase<P, S> {
  constructor(props?: P, state?: S) {

    // Auto-bind methods on the derived type so that the "this" reference is as expected
    // - Only do this the first time an instance of the derived class is created
    var autoBoundTypeScriptMethodsFlagName = '__autoBoundTypeScriptMethods';
    var autoBindMapPropertyName = '__reactAutoBindMap'; // This is an internal React value
    var cp = this['constructor'].prototype;
    var alreadyBoundTypeScriptMethods = (cp[autoBoundTypeScriptMethodsFlagName] === true)
      && cp.hasOwnProperty(autoBoundTypeScriptMethodsFlagName);
    if (!alreadyBoundTypeScriptMethods) {
      var autoBindMap = {};
      var parentAutoBindMap = cp[autoBindMapPropertyName];
      var functionName;
      if (parentAutoBindMap) {
        // Maintain any binding from an inherited class (if the current class being dealt
        // with doesn't directly inherit from ReactComponentBase)
        for (functionName in parentAutoBindMap) {
          autoBindMap[functionName] = parentAutoBindMap[functionName];
        }
      }
      for (functionName in cp) {
        if (!cp.hasOwnProperty(functionName) || (functionName === "constructor")) {
          continue;
        }
        var fnc = cp[functionName];
        if (typeof (fnc) !== 'function') {
          continue;
        }
        autoBindMap[functionName] = fnc;
      }
      cp[autoBindMapPropertyName] = autoBindMap;
      cp[autoBoundTypeScriptMethodsFlagName] = true;
    }

    this['construct'].apply(this, arguments); // This is an internal React method
  }

  props: P;
  state: S;
}
ReactComponentBase.prototype = React.createClass({
  // The component must share the "componentConstructor" that is present on the prototype of
  // the return values from React.createClass
  render: function () {
    return null;
  }
})['componentConstructor'].prototype; // Also an internal React method

// This must be used to mount component instances to avoid errors due to the type definition
// expecting a React.ReactComponent rather than a ReactComponentBase (though the latter is
// able to masquerade as the former and when the TypeScript compiles down to JavaScript,
// no-one will be any the wiser)
export function renderComponent<P, S>(
    component: ReactComponentBase<P, S>,
    container: Element,
    callback?: () => void) {
  var mountableComponent = <React.ReactComponent<any, any>><any>component;
  React.renderComponent(
    mountableComponent,
    container,
    callback
    );
}

This allows the following component to be written:

import React = require('react');
import ReactComponentBridge = require('components/ReactComponentBridge');

class MyButton extends ReactComponentBridge.ReactComponentBase<{ message: string }, any> {
  myButtonClickHandler(event: React.MouseEvent) {
    alert('Clicked MyButton with message "' + this.props.message + '"');
  }
  render() {
    return React.DOM.button({ onClick: this.myButtonClickHandler }, 'Click Me');
  }
}

export = MyButton;

which may be rendered with:

import ReactComponentBridge = require('components/ReactComponentBridge');
import MyButton = require('components/MyButton');

ReactComponentBridge.renderComponent(
  new MyButton({ message: 'Click Me' }),
  document.getElementById('container')
);

Hurrah! Success! All is well with the world! I've got the benefits of TypeScript and the benefits of React and the Flux architecture (ok, the last one doesn't need any of this or even require React - it could really be used with whatever framework you chose). There's just one thing..

I'm out of date

Like I said at the start of this post, as I got to rounding it out to publish, I realised that I wasn't on the latest version of React (current 0.11.2, while I was still using 0.10) and that this code didn't actually work on that version. Sigh.

However, the good news is that it sounds like 0.12 (still in alpha at the moment) is going to make things a lot easier. The changes in 0.11 appear to be paving the way for 0.12 to shakes things up a bit. Changes are documented at New React Descriptor Factories and JSX which talks about how the problem they're trying to solve with the new code is a

Simpler API for ES6 classes

.. and there is a note in the react-future GitHub repo ("Specs & docs for potential future and experimental React APIs and JavaScript syntax") that

A React component module will no longer export a helper function to create virtual elements. Instead it's the responsibility of the consumer to efficiently create the virtual element.

Languages that compile to JS can choose to implement the element signature in whatever way is idiomatic for that language:

TypeScript implements some ES6 features (such as classes, which are how I want to represent React components) so (hopefully) this means that soon-to-hit versions of React are going to make ES6-classes-for-components much easier (and negate the need for a workaround such as is documented here).

The articles that I've linked to (I'm not quite sure how official that all is, btw!) are talking about a future version since they refer to the method "React.createFactory", which isn't available in 0.11.2. I have cloned the in-progress master repo from github.com/facebook/react and built the 0.12-alpha code* and that does have that method. However, I haven't yet managed to get it working as I was hoping. I only built it a couple of hours ago, though, and I want to get this post rounded out rather than let it drag on any longer! And, I'm sure, when this mechanism for creating React components is made available, I'm sure a lot of information will be released about it!

* (npm is a great tool but it still can't make everything easy.. first I didn't realise that the version of node.js I was using was out of date and it prevented some dependencies from being installed. Then I had to install Python - but 2.7 was required, I found out, after I'd installed 3.4. Then I didn't have Git installed on the computer I was trying to build React from. Then I had to mess about with setting environment variables for the Python and Git locations. But it did work, and when I think about how difficult it would have been without a decent package manager I stop feeling the need to complain about it too much :)

Posted at 23:12

Comments

TypeScript State Machines

Last time, in C# State Machines, I used the example of traffic lights at a crossroads to illustrate how complexity could be handled easily with the state machine pattern. This was a follow-on to my Parsing CSS post and inspired by the excellent article Game Programming Patterns: State.

Well this little excursion offered me the perfect opportunity to dive into something else I've been looking at: TypeScript. I've long been a fan of JavaScript. It's got a lot of quirks and it's easy to write absolute rubbish with it. In fairness, it's possible to write crap with any language, it just feels like sometimes JavaScript makes it very easy. But if you embrace the patterns that work well with it and apply a little discipline, you can come up with elegant, maintainable solutions.

But I've often found it difficult to leave behind entirely the concept of static typing and that warm cozy feeling of knowing that the argument that you want is going to be an int and, as an int, you can apply any range checks you might need to and be content that everything has met your expectations (or demands, perhaps :) But if you don't even know what type it is, you can't as easily apply these sorts of restrictions. You can try to check the type of the argument and then apply range checking (or whatever) but that can start to get very messy, very quickly. I used to work with someone who claimed that static checking was unnecessary with sufficient unit testing. Meanwhile, I still think that there's a lot of potential for Code Contracts which is tending towards the polar opposite, so we basically had to agree to disagree on that one. In fact I may choose to take his argument to mean that with static types that a whole category of unit tests become unnecessary, handled instead by compiler checks!

So it's probably fairly easy to see, if I like JavaScript and I like static typing, why TypeScript could seem apper attractive prospect. And here was a nice little project that was non-trivial but still pretty compact that I could try it out with.

The type annotations in TypeScript are optional, but since I'm writing the code from scratch it makes sense to use them throughout. Interfaces are defined but do not need to referenced by types that implement the interface - so long as they have all of the required properties and methods then they will implicitly be considered to implement the interface. This is like Google's Go language and not like C#.

One thing that I don't like too much is that it isn't currently possible to specify an interface with readonly properties. It must be a property with a getter and setter or nothing. So I've resorted to specific Get methods on the interfaces instead, such as GetColour() and GetState().

(I'm going for a fairly direct port from the C# code. If you haven't read the last post then it might be worth a quick look - there's nothing too complicated going on here, it's just that I'm going to be skimming over the general program structure and concentrating mostly on how it works with TypeScript).

interface IAmATrafficLightState {
  GetColour(): ColourOptions;
  GetStatus(): StatusOptions;
  RegisterCarQueueing(): IAmAStateTransition;
  RegisterPassageOfTime(): IAmAStateTransition;
}

interface IAmAStateTransition {
  GetTransitionType(): TransitionTypeOptions;
  GetNewState(): IAmATrafficLightState; 
}

enum ColourOptions {
  GreenOnly,
  RedAndYellow,
  RedOnly,
  YellowOnly
}

enum StatusOptions {
  HandlingTraffic,
  NotHandlingTraffic
}

enum TransitionTypeOptions {
  NoChange,
  Pop,
  Push,
  Replace
}

In the C# code, I actually had a StateTransition class rather than an IAmAStateTransition. The class had a private constructor and multiple static public methods for constructing instances: NoChange, Pop, Push and Replace. This isn't a structure that TypeScript supports, so instead I've got an interface and separate implementations. Each of the below classes implicitly IAmAStateTransition as they have the methods GetTransitionType() and GetNewState().

The PushTransition and ReplaceTransition classes take a single constructor argument of type IAmATrafficLightState (as the transitions work against a "state stack", only the Push and Replace actions require a state to change to, Pop and NoChange don't). Since the constructor argument's type is specified, the only validation I have to perform to remain consistent with the C# code is to ensure that it has a non-null value. TypeScript would indicate a compile-time error if I tried to pass a string for this argument, as that clearly isn't an IAmATrafficLightState implementation. But it won't complain about either a null value or an undefined value. So the easiest thing to do seems to be just use the JavaScript pattern of testing for anything that evaluates to false - ie. "if (!state) { /* Error */ }"

class NoChangeTransition {
  GetTransitionType(): TransitionTypeOptions {
    return TransitionTypeOptions.NoChange;
  }
  GetNewState(): IAmATrafficLightState {
    return null;
  }
}

class PopTransition {
  GetTransitionType(): TransitionTypeOptions {
    return TransitionTypeOptions.Pop;
  }
  GetNewState(): IAmATrafficLightState {
    return null;
  }
}

class PushTransition {
  constructor(private state: IAmATrafficLightState) {
    if (!state) {
      throw new Error("state may not be null for a Push Transition");
    }
  }
  GetTransitionType(): TransitionTypeOptions {
    return TransitionTypeOptions.Push;
  }
  GetNewState(): IAmATrafficLightState {
    return this.state;
  }
}

class ReplaceTransition {
  constructor(private state: IAmATrafficLightState) {
    if (!state) {
      throw new Error("state may not be null for a Replace Transition");
    }
  }
  GetTransitionType(): TransitionTypeOptions {
    return TransitionTypeOptions.Replace;
  }
  GetNewState(): IAmATrafficLightState {
    return this.state;
  }
}

I like the syntax here where constructor arguments can be marked as private, resulting in a private backing field being implicitly specified (see Steve Fenton's Stop Manually Assigning TypeScript Constructor Parameters). What I don't like is that in the resulting JavaScript, these fields are not private. If you look at the JavaScript below, which is generated from the TypeScript PushTransition class..

// This is the generated JavaScript for the TypeScript "PushTransition" class above
var PushTransition = (function () {
  function PushTransition(state) {
    this.state = state;
    if (!state) {
      throw new Error("state may not be null for a Push Transition");
    }
  }
  PushTransition.prototype.GetTransitionType = function () {
    return TransitionTypeOptions.Push;
  };
  PushTransition.prototype.GetNewState = function () {
    return this.state;
  };
  return PushTransition;
})();

.. you'll see that the state value is stored in "this.state". That's a public reference that JavaScript can manipulate. If all of the code that uses this class is TypeScript, then it won't be a problem as the compiler will enforce its private status. But if this is code to be called by non-TypeScript JavaScript then it's not ideal.

Although Douglas Crockford showed us years ago that genuinely private members were possible (see Private Members in JavaScript), the sacrifice is that methods for an object instance with private members must be declared for each instance. The class structure used by TypeScript, on the other hand, uses the prototype approach to declare functions for each class. This means that each method is defined only once per class, rather than once per instance. So it's a conscious decision to gain a performance improvement in terms of the memory required. (Anders Hejlsberg - the daddy of both C# and TypeScript - addresses exactly this point in this forum post Private Variables).

Time-Transitioning States

Some states in this model will transition based upon time alone. The initial state of RedLightWaitingForTraffic will only transition when cars arrive at the lights whereas states such as GreenLight transition based solely on time - it will stay green for a set period of time before cycling back round.

In the C# code last time, I had an abstract TimeBasedTransitiveState class with a nested class that would represent the states during which time was being counted down before the next transition. This nested class would have a "Source" property that pointed back to the traffic light state that started the countdown (eg. a GreenLight instance). TypeScript doesn't support abstract classes or nested classes so this structure wasn't going to work.

Instead I wrote it in a much more straight forward manner and then replaced the classes that have no internal state other than Colour, Status, Next Transition and Time-To-Next-Transition with what amount to static references. I liked this approach so much that I went back and changed the C# code such that TimeBasedTransitiveState class was written in pretty much the same way. (I've put the code up on Bitbucket for reference - see the TrafficLightStateMachine). I haven't changed the C# code to use static references yet, but it's something I'm considering.

class TimeBasedTransitiveState {
  constructor(
    private colour: ColourOptions,
    private status: StatusOptions,
    private timeSlicesToWaitFor: number,
    private nextTransition: IAmAStateTransition) {
    if (!nextTransition) {
      throw new Error("nextTransition may not be null for a Push Transition");
    }
    if (timeSlicesToWaitFor <= 0) {
      throw new Error("timeSlicesToWaitFor must be a positive value");
    }
  }
  GetColour(): ColourOptions {
    return this.colour;
  }
  GetStatus(): StatusOptions {
    return this.status;
  }
  RegisterCarQueueing(): IAmAStateTransition {
    return new NoChangeTransition();
  }
  RegisterPassageOfTime(): IAmAStateTransition {
    if (this.timeSlicesToWaitFor === 1) {
      return this.nextTransition;
    }
    return new ReplaceTransition(
      new TimeBasedTransitiveState(
        this.colour,
        this.status,
        this.timeSlicesToWaitFor - 1,
        this.nextTransition
      )
    );
  }
}

var RedLightPausedBeforeWaitingForTraffic = (function () {
  var TIME_AFTER_RESETTING_TO_RED_BEFORE_CONSIDERING_TRAFFIC = 5;
  return new TimeBasedTransitiveState(
    ColourOptions.RedOnly,
    StatusOptions.HandlingTraffic,
    TIME_AFTER_RESETTING_TO_RED_BEFORE_CONSIDERING_TRAFFIC,
    new PopTransition()
  );
})();

var YellowLight = (function () {
  var TIME_TO_WAIT_ON_YELLOW = 5;
  return new TimeBasedTransitiveState(
    ColourOptions.YellowOnly,
    StatusOptions.HandlingTraffic,
    TIME_TO_WAIT_ON_YELLOW,
    new ReplaceTransition(RedLightPausedBeforeWaitingForTraffic)
  );
})();

var GreenLight = (function () {
  var TIME_TO_STAY_ON_GREEN = 100;
  return new TimeBasedTransitiveState(
    ColourOptions.GreenOnly,
    StatusOptions.HandlingTraffic,
    TIME_TO_STAY_ON_GREEN,
    new ReplaceTransition(YellowLight)
  );
})();

var RedAndYellowLight = (function () {
  var TIME_TO_WAIT_ON_RED_AND_YELLOW = 5;
  return new TimeBasedTransitiveState(
    ColourOptions.RedAndYellow,
    StatusOptions.HandlingTraffic,
    TIME_TO_WAIT_ON_RED_AND_YELLOW,
    new ReplaceTransition(GreenLight)
  );
})();

var RedLightAboutToChange = (function () {
  // We're committed to letting traffic pass at this point so declare HandlingTraffic
  var TIME_TO_STAY_RED_AFTER_CAR_ARRIVES = 10;
  return new TimeBasedTransitiveState(
    ColourOptions.RedOnly,
    StatusOptions.HandlingTraffic,
    TIME_TO_STAY_RED_AFTER_CAR_ARRIVES,
    new ReplaceTransition(RedAndYellowLight)
  );
})();

Note that the state objects have to follow the TimeBasedTransitiveState definition, in terms of the order in which they appear in the code. These objects are created by calling the constructor of the TimeBasedTransitiveState class. If these calls are made before the class is defined then an error such as "undefined is not a function" or "PushTransition is not a constructor" will be raised (depending upon browser or other runtime environment).

This has been observed by many people, sometimes resulting in bug reports such as Inheritance only works if parent class is declared before child class. These have so far been rejected. In that report, Jon Turner writes

Unless we reorder the code for you, I think you still can come up with situations where a value hasn't been fully initialized. [..] At this time, we're explicitly not reordering code (or even adding code except in a couple of exceptions) that you've written.

So I guess that we have to get used to the current situation.

For working with multiple files, there is support for CommonJs and AMD modules as talked about at Organizing your code with AMD modules and require.js. Note that I think the mention of comments "reference path" is out of date now, as Steve Fenton points out in Say Goodbye To TypeScript Reference Comments!

One last point relating to this. I had a niggling thought that "isn't hoisting in JavaScript a way around this?" But hoisting is just about variable declarations, not their definitions. If you need a refresher on this (like I did) then this article is excellent: JavaScript Hoisting Explained (it has a video at the top which I skipped, all of the information is written below it).

Traffic-Transitioning States

So back to the TypeScript state machine code..

The traffic-transitioning states are the ones that are a bit more interesting! Traffic lights start off as a RedLightWaitingForTraffic. Once traffic is registered as having arrived at the light, it will transition to either the RedLightAboutToChange state or the RedLightWaitingForAccess. The first option is only possible if the traffic lights on the intersecting road at the crossroads are not letting traffic through - it would be no good for traffic on both roads to be moving simultaneously! The RedLightAboutToChange is one of the time-transitioning states above, all that will happen is that the full state cycle (RedAndYellow to Green to Yellow and back to Red) will take place.

However, if the other road is letting through traffic then the RedLightWaitingForAccess state is used. This state will check whether it is free to pass traffic every time that its RegisterPassageOfTime method is called. If so (meaning that the other road is no longer letting traffic flow), then it can transition straight to the RedAndYellowLight state. Otherwise it has to stick to being a RedLightWaitingForAccess.

Something I particularly liked when writing the TypeScript version was how easy it was to specify a constructor argument that was a function. I shouldn't be surprised, really, since not only does JavaScript support first class functions but also C# has had lambdas all over the place (and the Func class) since .net 3.5. But it was just gratifying that it was so easy to declare! I want a single argument that is a parameter-less function that returns a bool. As such, I need only write -

constructor(private isAllowedToLetTrafficThrough: () => boolean) {

Lovely! Succinct but easy to follow. So for the full implementations of the two traffic-based states we have -

class RedLightWaitingForAccess {
  constructor(private isAllowedToLetTrafficThrough: () => boolean) {
    if (!isAllowedToLetTrafficThrough) {
      throw new Error("isAllowedToLetTrafficThrough must be specified");
    }
  }
  GetColour(): ColourOptions {
    return ColourOptions.RedOnly;
  }
  GetStatus(): StatusOptions {
    return StatusOptions.NotHandlingTraffic;
  }
  RegisterCarQueueing(): IAmAStateTransition {
    // We can't do anything here, we're already waiting
    return new NoChangeTransition();
  }
  RegisterPassageOfTime(): IAmAStateTransition {
    if (this.isAllowedToLetTrafficThrough()) {
      return new ReplaceTransition(RedAndYellowLight);
    }
    return new NoChangeTransition();
  }
}

class RedLightWaitingForTraffic {
  constructor(private isAllowedToLetTrafficThrough: () => boolean) {
    if (!isAllowedToLetTrafficThrough) {
      throw new Error("isAllowedToLetTrafficThrough must be specified");
    }
  }
  GetColour(): ColourOptions {
    return ColourOptions.RedOnly;
  }
  GetStatus(): StatusOptions {
    return StatusOptions.NotHandlingTraffic;
  }
  RegisterCarQueueing(): IAmAStateTransition {
    if (this.isAllowedToLetTrafficThrough()) {
      return new PushTransition(RedLightAboutToChange);
    }
    return new PushTransition(new RedLightWaitingForAccess(this.isAllowedToLetTrafficThrough));
  }
  RegisterPassageOfTime(): IAmAStateTransition {
    return new NoChangeTransition();
  }
}

Representing the state transitions as a stack and having each IAmATrafficLightState implementation return an IAmAStateTransition for the calls to RegisterCarQueueing and RegisterPassageOfTime makes following the changes in state very easy. The RedLightWaitingForTraffic is always at the bottom of the stack. When it changes state (to either a RedLightAboutToChange or a RedLightWaitingForAccess) that new state is pushed onto the stack. All of the following states replace that top entry until the final RedLightPausedBeforeWaitingForTraffic which will pop off, leaving the original RedLightWaitingForTraffic.

Tying it all together

Having recreated the states and the transitions, we need the TrafficLight class that keeps track of the state queue. The TypeScript version is reassuringly close to the C# code. There's no Stack class so I've used an array instead (which in JavaScript has "push" and "pop" methods and so isn't far removed from the .net Stack class). TypeScript's enums are implemented in such a way that if you want to display their name (rather than their numeric value) then you need to treat the enum as a hashtable which maps the value back onto the name. This varies from C#, where the ToString method of an enum value will return the name rather than the value. Also note that I'm using a method "Log" to write out messages. This will be defined below.

class TrafficLight {
  private states: IAmATrafficLightState[];
  constructor(private trafficLightId: string, initialState: IAmATrafficLightState) {
    if (!trafficLightId) {
      throw new Error("A trafficLightId must be specified");
    }
    if (!initialState) {
      throw new Error("An initialstate must be specified");
    }
    this.states = [ initialState ];
  }

  GetTrafficLightId(): string {
    return this.trafficLightId;
  }

  GetColour(): ColourOptions {
    return this.GetCurrentState().GetColour();
  }

  GetStatus(): StatusOptions {
    return this.GetCurrentState().GetStatus();
  }

  RegisterPassageOfTime(): void {
    this.ApplyTransition(this.GetCurrentState().RegisterPassageOfTime());
  }

  RegisterCarQueueing(): void {
    this.ApplyTransition(this.GetCurrentState().RegisterCarQueueing());
  }

  private ApplyTransition(transition: IAmAStateTransition): void {
    var previousColour = this.GetCurrentState().GetColour();
    if (transition.GetTransitionType() === TransitionTypeOptions.NoChange) {
      // Do nothing
    }
    else if (transition.GetTransitionType() === TransitionTypeOptions.Pop) {
      if (this.states.length === 1) {
        throw new Error("Invalid transition - may not remove last state in the stack");
      }
      this.states.pop();
    }
    else if (transition.GetTransitionType() === TransitionTypeOptions.Push) {
      this.states.push(transition.GetNewState());
    }
    else if (transition.GetTransitionType() === TransitionTypeOptions.Replace) {
      this.states[this.states.length - 1] = transition.GetNewState();
    }
    else {
      throw new Error("Unsupported transition type: " + transition.GetTransitionType());
    }
    var newColour = this.GetCurrentState().GetColour();
    if (newColour !== previousColour) {
      Log(
        "* " + this.trafficLightId + " changed " + ColourOptions[previousColour] +
        " to " + ColourOptions[newColour]
      );
    }
  }

  private GetCurrentState() {
    return this.states[this.states.length - 1];
  }
}

The final piece of the puzzle is the equivalent of the Program class that drives the simulation in the C# code. This will create an object "tester" that wraps up references to both the North-South and East-West traffic lights in a closure, exposing a method "Advance" which will call "RegisterPassageOfTime" on the lights and, from time-to-time, based on the probabilityOfCarArrivingEachTimeSlice value, call "RegisterCarQueueing" too.

I originally intended to run the code from the command line (using CScript) but thought afterward that it might be worth doing in the browser as well, maybe using that to bolt on some sort of graphical representation of what's happening. Doing it in the browser allows it to be slowed down, too, since tester.Advance may be called through the setInterval method rather than firing full speed as it does at the command line (JavaScript has no Thread.Sleep method!). Note that any sort of "graphical representation" is yet to be implemented, it's text-only for now.

Whether or not it's being hosted in the browser also affects how it logs out its messages - with "console.log" or "WScript.Echo". I've gone for a simple approach in guessing how it's being hosted by presuming that if there is a "window" reference that it's in the browser and at the command line otherwise.

var tester = (function () {
  var probabilityOfCarArrivingEachTimeSlice = 0.1;

  var eastWestTrafficLight: TrafficLight = null;
  var northSouthTrafficLight = new TrafficLight(
    "N-S",
    new RedLightWaitingForTraffic(
      function() {
        return eastWestTrafficLight.GetStatus() === StatusOptions.NotHandlingTraffic;
      }
    )
  );
  eastWestTrafficLight = new TrafficLight(
    "E-W",
    new RedLightWaitingForTraffic(
      function() {
        return northSouthTrafficLight.GetStatus() === StatusOptions.NotHandlingTraffic;
      }
    )
  );

  var allTrafficLights = [ northSouthTrafficLight, eastWestTrafficLight ];
  return {
    Advance: function () {
      for (var i = 0; i < allTrafficLights.length; i++) {
        var trafficLight = allTrafficLights[i];
        if (Math.random() < probabilityOfCarArrivingEachTimeSlice) {
          if (trafficLight.GetColour() === ColourOptions.GreenOnly) {
            Log(
              "Car didn't have to queue " + trafficLight.GetTrafficLightId() +
              ", went straight through"
            );
          }
          else if (trafficLight.GetColour() === ColourOptions.YellowOnly) {
            Log(
              "Car didn't have to queue " + trafficLight.GetTrafficLightId() +
              ", went straight through (naughty!)"
            );
          }
          else {
            Log("Register car queueing " + trafficLight.GetTrafficLightId() + "..");
          }
          trafficLight.RegisterCarQueueing();
        }
      }
      for (var i = 0; i < allTrafficLights.length; i++) {
        allTrafficLights[i].RegisterPassageOfTime();
      }
    }
  };
})();

function IsBrowser(): boolean {
  return (typeof(window) !== "undefined");
}

function Log(message): void {
  if (IsBrowser()) {
    console.log(message);
  }
  else {
    WScript.Echo(message);
  }
}

if (IsBrowser()) {
  setInterval(tester.Advance, 100);
}
else {
  while (true) {
    tester.Advance();
  }
}

So how did TypeScript treat me?

All in all, I've enjoyed this. There were some potential gotchas like the ordering of classes. There's the concern over the public privates. There's no abstract classes or a "protected" keyword, nor are there nested classes, nor can you declare consts. Buut none of these are the end of the world.

When I first tried it out, for some reason I thought that the default behaviour of "==" and "!=" were going to be changed to act as "===" and "!==". This is not the case and I don't think it's the case for Dart or CoffeeScript either. It is the case for GorillaScript, which I read about last month. This also has support for optional typing (like TypeScript) but also throws in a lot of other features such as the equality changes I just mentioned (if you really want the JavaScript "==" and "!=" behaviour then you can use "~=" and "!~=" which I'm choosing to read as "wobbly equals" - which seems appropriate!), immutable-by-default (which I love the sound of), constants, generics, promises, all sorts! It even - hold onto your hats for controversy time - uses indentation-based code blocks, rather than curlies (now you know it's crazy! :) I don't know what uptake for this has been like or what sort of support is available but I may well be having a bit of a poke into this at some point.

Back to TypeScript.. I'm fairly sure that this is going to be just an early foray into its abilities for me. I've really liked what I've seen so far and hope to make time to try to use it in more scenarios. Something I really liked, that I found myself doing almost unconsciously to begin with, was not using hungarian notation in the code. Now I know that the idea that I used it at all will make some people a little sick in their mouth but I did feel that it acted as a crutch to document some of my intent when writing in a language without type annotations. I don't do it in C#. And I don't do it in TypeScript!

Posted at 21:03

Comments