React <3 TypeStript

You will find the example code on this repository

Note: This article will require a basic understanding of TypeScript features.

React is a very productive framework. We allmost use it with the Webpack + Babel build environnent, but the productivity of the developer can be greatly improved by switching Babel for TypeScript !

Why TypeScript ?

There are many reasons why TypeScript realy speedup development :

TypeScript fails faster

When a TypeScript code compile, you are 100% sure to have a working compiled code. With TypeScript, if your code is well typed, every lines of code gets validated by the compiler before getting transformed in javascript. Any error will be notice immediatly. It a nice gain of time.

TypeScript code is more predictable and easier to debug than standard JavaScript.

Tools are awesome

Java and .Net developers may laugth, but for a front-end developer it's awesome to finely have autocompletion, jump to definition, dependency map, refactoring etc...

I think tooling is still a big problem in front end development, TypeScript solve this problem. Just try Visual Studio Code with TypeScript, juste awesome !

(News of the month: Visual Studio Code will now use the TypeScript engine for javascript tooling, javasctipt developers will now have benefit of the TypeScript technology ! :) )

Typing enhance documentation

TypeScript code is self documented by the typings and the auto-completion tools.

More over, TypeScript teach you a lot about the code you are using an thus improve your global javascript knowledge !

Example:

function foo (agr: string): bool {  
...
}

No more need to go to the function declaration and to decript the function to know what argument is needed and what is the type of the output. Your IDE will tell you everything when writing your code.

Even if someone get back to your code months after, everything will stay crystal clear !

Project configuration

Webpack

The ideal toolset will be classic : npm - WebPack - Karma

When working with React and TypeScript, you can safely pick a standard React es6 project, and drop every babel loaders in flavor of ts-loader.

ts-loaded can convert jsx (tsx) to javascript, so no need for additional loaders !

That done, everything should work out of the box.

Typings

To get the full benefit of typing you shall use tsd, a nice typing library for TypeScript projects. Typings will allows you to get the typings of installed libraries like React, Flux, Immutable, and manage then with a nice cli tool (think npm for typings)...

React and Typescript

React plays very nicely with TypeScript. Moreover, like for javasript version, React modern architecture allows me to learn some advanced features of TypeScript.

Let's see how to develope a React component with TypeScript.

Let's begin by creating a .tsx file, the TypeScript equivalent of .jsx. Then the first thing to do is to add a link to the tsd files like so :

/// <reference path="../typings/tsd.d.ts" />

This will provide all typings defined by the tsd libraries.

Always start from ES6 javascript

TypeScript is nothinng more than a javascript superset. Thus, a good thing to remember: valid javascript will be valid typesript.

Our component will look prety familiar :

import * as React from "react"

export default class HelloComponent extends React.Component {  
  render () {
    return (
      <div>
        <h1>Hello TypeScript !</h1>
      </div>
    )
  }
}

Then add some typings

Well, all TypeScript power comes from its typing. But how to type a React Component ?

interface P {};  
interface S {};

export default class HelloComponent extends React.Component<P, S> {  
  render (): React.ReactElement<{}> {
    return (
      <div>
        <h1>Hello TypeScript !</h1>
      </div>
    );
  }
};

If it looks like viking runes to you, it's normal, we will here learn some interesting advanced TypeScript features. Let's decompose :

React.Component<P, S>  

The strange angles brackets are what we will call generic types. It similar to generic types in java and c#, and will allow us to use reusable interfaces, like the React.Component one !

Let's have a look to the React.Component interface for a better understanding :

class Component<P, S> implements ComponentLifecycle<P, S> {  
    constructor(props?: P, context?: any);
    setState(f: (prevState: S, props: P) => S, callback?: () => any): void;
    setState(state: S, callback?: () => any): void;
    forceUpdate(callBack?: () => any): void;
    render(): JSX.Element;
    props: P;
    state: S;
    context: {};
    refs: {
        [key: string]: ReactInstance
    };
}

Has you see, P and S types are reinjected as type definitions for the props and the state of our component. This is pretty awesome because it allows us to have fine tuned typings on both state and props of our Component, a any place where they are used : Render function, contructor, and even in jsx when we will later include our component in a template !

It very awesome with React components because it simplify documentation and props validation. Everyone could use your component without fear to broke the app and without spending hours to look for documentation !

So lets add some props and some state properties to our component :

interface P {  
  title: string
};
interface S {  
  timer: int
};

export default class HelloComponent extends React.Component<P, S> {  
  constructor (props: P) {
    super(props)

    this.state = {
      timer: 0
    }

    window.setInterval((): void => {
      this.setState({
        timer: this.state.timer + 1
      });
    });
  }

  render (): React.ReactElement<{}> {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <div>Time: {this.state.timer}</div>
      </div>
    );
  }
};

The idea is the same for React.ReactElement<{}>. You can provide to React.ReactElement additionnal props.

A note on props interface

In order to be able to reiceive some special props like key or ref, the props interface P should extend React.Props like so :

interface P extends React.Props<{}> {  
  ...
}

Handling events

One painfull point I had using React with TypeScript is dealing with the event argument.

A quick example, let's do this :

export default class HelloComponent extends React.Component<P, S> {  
  handleChange (e: React.FormEvent): void {
    let value = e.target.value;
  }
};

If you compile this, you will ave an error because React.MouseEvent.target does not have a full HTMLDomElement interface.

The trick is to use the typescript as keywork, to assign the native type HTMLInputElement to our new variable.

export default class HelloComponent extends React.Component<P, S> {  
  handleChange (e: React.FormEvent): void {
    const elm = e.target as HTMLInputElement;
    let value = e.target.value;
  }
};

Now our code will be valid and you will have access to the full native DOM api in autocompletion.

Dealing with context switching

TypeScript offers a very nice feature to help us deal with the problem of context switching in components :

Lambda methods

These allows us to declare metod with a fixed this context like so :

export default class HelloComponent extends React.Component<P, S> {  
  handleChange: (e: React.FormEvent) => void = (e: React.FormEvent): void => {
    let value = e.target.value;
  }
};

It's a little big verbose, but it allows us to not write this.handleChange = this.handleChange.bind(this) for every method in the component contructor,
TypeScript take case of this for us.

Stateless components

Since v1.8 TypeScript support stateless component. Creation is far more easy than classic components.

interface P extends React.Props<{}> {  
  text: string
}

export default (props: P): React.ReactElement<P> => {  
  return (
    <p>{props.text}</p>
  );
}

The only tricky part is to provide the right type to the component: React.ReactElement<P>. It's the same type than the classic render method of a component.

Well, now that you know the basic of React development with TypeScript, It's time for you to just ave fun and enjoy it !