/ typescript

The joys of moving React & Redux to Typescript

So I've just started teaching myself React and Redux. A great stack, but all the tutorials I have found focus more on ES6 rather than Typescript. No issue, I'll start with that, then I have a base to move it. Yeah, not very trivial at all.

So far the best tutorial I have found (also very lengthy) was at Full-Stack Redux Tutorial which was amazing. Based around TDD (Test driven development) which I still haven't moved to yet (another article coming about that soon enough).

I'll probably end up writing this up as a proper tutorial at some stage, but essentially my task was first to move it to more modern practices (using ES6 classes rather than React's now depricated createClass function).

Next was updating all the libraries used in that tutorial and fixing anything broken by that. Webpack being the largest change (Webpack 1 to 2). React-Router being the other major change.

Then the big task started, Typescript and full type safety.

As may readers of this may know, any Javascript file is a valid Typescript file, so renaming *.js to *.ts along with *.jsx to *.tsx. Having a matching tsconfig.json that allows this takes away a lot of the pain. The main points being

{
    "noImplicitAny": false,
    "strict": false,
    "strictNullChecks": false
}

From here, it should compile.

Next up is turning on

{
    "noImplicitAny": true,
    "strict": true,
}

and then going through, decorating all variables throwing errors with : any as well as required generics <any>.

My ultimate goal is to remove as many of those any's as possible. On the server side, I have one left, on the client, quite a few more, mainly due to the react-redux connect() function. I may be able to narrow this down further. Also, standard React interfaces tend to have a children?: any in them. This is accepted practice.

So given that there are advantages of Facebook's immutable.js library over Typescript interfaces with readonly variables, I went that way.

Reasearch lead me to Immutable's Record class which is basically a Map with specifications on what fields are allowed.

I'm actually quite proud of this one (apart from the hack in toJS()), from my server code:-

import { Record, List, Map } from 'immutable';
import { SET_ENTRIES, VOTE, NEXT } from './constants';

export type VOTING_SERVER_STATE = {
    entries?: List<string>;
    vote?: string;
    winner?: string;
    entry?: string;
    pair?: List<string>,
    tally?: Map<string, number>
}

export class VotingServerState extends Record({entries: undefined, vote: undefined, winner: undefined, entry: undefined, pair: undefined, tally: undefined} as VOTING_SERVER_STATE) {
    public toJS(): any {
        // Clear out undefined values
        return JSON.parse(JSON.stringify(super.toJS()));
    }
}

export const INITIAL_STATE = new VotingServerState();

All fields are optional, but they're the only ones allowed. This is a very nice way to get develop time checking (even better than compile time).

Getting all the types and Records sorted was probably the biggest headache from this, especially client side and mixing it with React, but looking through the code now makes a lot more sense.

So after making sure all the tests passed (a lot of hacking around in here, initially until I got my head into the right space, in particular overriding the toJS() function).... it didn't work (well, actually the server worked perfectly)...

Argh. Ok yes, idiot needs more tests (as of writing this, I haven't written them yet... bad developer -slap-), but I hadn't hooked up the dispatch functions in Redux (the joys of bouncing around varying tutorials picking parts from here and there to understand it).

And finally... IT WORKS!!! Yay.

I should probably clean up the code a bit more (and write more tests), but if you're curious of the final code, go check out these to Github repos.

Voting-Server
Voting-Client