Todd Lucas

Software developer, etc.

React and TypeScript

TypeScript was recently updated to version 1.6, which adds support for React JSX syntax. I've put together a starter project on Github that makes use of it.

Whenever I have an idea for a project that I'd like to prototype, it's nice to be able to experiment with it quickly. Getting Node.js set up with all the pieces that I like to play with can be a bit time consuming. This starter takes that process down to a matter of minutes.

Isomorphism

One of the big reasons that many people use Node is that the same code can run on the server and in the browser. React supports this so-called isomorphic modality well. To achieve isomorphism, a few things are necessary.

First, rendering on the client and server must be supported. React is primarily a client-side framework, but it supports rendering on the server with the renderToString method.

Next, the code must be packaged differently for the browser. This starter project uses Browserify, which allows modules written for the Node environment to be converted for use in the browser. It locates dependencies and packages up all relevant modules into a single JavaScript file, with proper module isolation.

Finally, the server must be able to render any page when the user refreshes, or navigates directly. React Router is the standard here. It allows pseudo-navigation client side using the HTML5 History API, as specified in a routing table. If a page is refreshed, the same routing table can be used on the server to render the equivalent page.

TSX

The new JSX support in TypeScript 1.6 is a big change. When you combine TypeScript with React JSX syntax, you can use the new .tsx file extension. Building React components using ES6 syntax with type checking is a major improvement. Tools like Visual Studio Code can take advantage of the compiler and typing information to provide code completion. This makes writing JavaScript a much more productive and positive experience for those of us who are used to this editing environment. Here's an example:

import * as React from 'react';

export default class AboutView extends React.Component<any, any> {
    render() {
        return <div>
            <h1>Example</h1>
        </div>;
    }
}

If your component takes props or state, you can define an interface to represent them. The two any template parameters provide defaults.

import * as React from 'react';

export interface IAboutViewProps {
    title: string;
}

export default class AboutView extends React.Component<IAboutViewProps, any> {
    render() {
        return <div>
            <h1>{this.props.title}</h1>
        </div>;
    }
}

If you don't use the expected props, you'll get a compile-time error. Also, if a parent doesn't specify a required prop, the compiler will tell you that it's missing. This can be very useful as a project grows or if components are refactored.

Building

The starter project uses Gulp as the build system. It includes a gulpfile that has a number of nice features. All the source is in one directory, src. During the build, this is transformed into a parallel directory, called www.

Static files are copied, and JS and CSS files are minified. If you use Less, the .less files are compiled and minified.

Browserification

As mentioned earlier, the build process browserifies the client code. Some libraries can be referenced externally, via globals. jQuery is a good example, with its $ object. Other libraries are referenced through requires or import statements. Any such reference will normally cause the entire library to be pulled in. This can result a huge download and a slow build process.

This gulpfile breaks the build into three parts. First, required 3rd party libraries are browserified into a separate vendor.js file. Second, certain 3rd party libraries, such as jQuery, are excluded using browserify-shim. These files are included via normal script elements. Making a library external like this requires changes in three places:

Finally, the remainder of the client files are packaged into app.js. This focus means that only one file needs to be rebuilt most of the time.

Watchers

Any time file is edited, a Gulp watcher will just build the piece that changed. This can help minimize the edit/reload cycle time. Keeping app.js small also helps.

Try it out

I hope that this will be useful to others who want to try TypeScript with React. Let me know what you think.

blog comments powered by Disqus