Bug Collection

(not a Pokémon.)

I keep every bug I’ve written. That first unescaped SQL query sits on a pedestal next to a card catalog bulging with cross-site scripting vectors. There’s a mishandled UTC offset pinned to the cork-board by the door. Step inside, and the portraits of race conditions line the hall.

Bugs are as much a part of software as developers and hardware. Even with increasingly ironclad assurances to the contrary, the messy combination of I/O, complex systems, and human fallibility will surely continuing breeding specimens for the collection in the months and years ahead.

And that’s kind of its point. The collection’s there to be studied. It’s not a pile of merde hacked together in a fit of masochistic glee: it’s a laundry list of real, honest-to-goodness bugs discovered during the post-mortem shakedown of well-intentioned production software.

Wrote it, broke it, fixed it.

Learned something.

Moved along.



TypeScript, Redux, and React

Note: this is the second entry in a short series about bolstering Redux application with TypeScripts. Part one introduced a simple counter application; next is to add a simple, React-based UI. Read on!

A web application without a UI isn’t much to look at. We previously used TypeScript to add static type-checking to a redux application, but with I/O limited to console.log its usability left something to be desired.

We can fix at least one shortcoming by adding a React-powered view layer. We’ll also get to see how TypeScript works out in a real (if trivial) interface, and use its static type-checking to verify our work at compile-time.

If you’re just interested in the results, check out the finished counter over on github. Otherwise, it’s time to get to work.

Setting up

We’ll need a few small new dependencies on top of our base redux project to get things working with react.

$ npm install --save-dev react react-dom react-redux

Since this is TypeScript, we’ll also need type definitions for any packages that don’t include them. At time of writing, that’s pretty much everything except redux, but there are community-supported alternatives available from npm and the DefinitelyTyped project for most of it. We’ll use typings to download them:

$ npm install -g typings
$ typings init
$ typings install --save --global dt~react
$ typings install --save --global dt~react-dom
$ typings install --save npm~react-redux

We now have a typings.json and a typings directory containing the updated definitions. Next, manually edit tsconfig.json to enable JSX and include the root typings definition:

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "ES5",
    "jsx": "react"
  },
  "files": [
    "typings/index.d.ts"
  ],
  "exclude": []
}

Finally, we need to tweak the webpack configuration to handle .tsx files. There’s a detailed guide over in the typescript handbook, but to get up and running we can update webpack.config.js to read:

module.exports = {
  entry: './src/index.tsx',
  output: {
    filename: './dist/bundle.js',
  },
  resolve: {
    extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js']
  },

  module: {
    loaders: [{ test: /\.tsx?$/, loader: 'ts-loader' }]
  },

  externals: {
    'react': 'react',
    'react-dom': 'reactdom'
  }
};

Components

A counter is simple enough. We’ll need a label (“what are we counting?”), a display (“what’s the count?”), and some way to increment it (“how does the count change?”). We can provide them with a simple component implementation in src/components/counter.tsx:

import * as React from 'react'

interface OwnProps {
  label: string
}

interface ConnectedState {
  counter: { value: number }
}

interface ConnectedDispatch {
  increment: (n: number) => void
}

interface OwnState {}

class CounterComponent extends React.Component<ConnectedState & ConnectedDispatch & OwnProps, OwnState> {

  _onClickIncrement = () => {
    this.props.increment(1)
  }

  render () {
    const { counter, label } = this.props
    return <div>
      <label>{label}</label>
      <pre>counter = {counter.value}</pre>
      <button ref='increment' onClick={this._onClickIncrement}>click me!</button>
    </div>
  }
}

The component’s internal state is easy: we won’t be maintaining any. OwnState is an empty interface included for demonstration, but we could just as easily satisfy TypeScript with an anonymous object:

class CounterComponent extends React.Component<..., {}> {
  // ...
}

The props are a bit more involved. this.props will be an object implementing the merged ConnectedState, ConnectedDispatch, and OwnProps interfaces. The separation may seem a bit excessive for such a trivial component, but–as we’ll see in a moment–there’s a reason for it.

Even though we haven’t outlined where the props come from yet, they’re clearly setting up for something. “Something” is a connection between CounterComponent and the application store, and as soon as we add in react-redux, some magical things will happen.

Connection

Here’s the plan. react-redux exposes a Provider component that–when supplied with a redux store–will allow container components wrapped to hook into the redux data flow. All we need to do is wrap the component in the connect method and implement two hooks to interact with the application:

import * as React from 'react'
import * as redux from 'redux'
import { connect } from 'react-redux'

import { incrementCounter } from '../actions'
import { Store } from '../reducers'

const mapStateToProps = (state: Store.All, ownProps: OwnProps): ConnectedState => ({
  counter: state.counter,
})

const mapDispatchToProps = (dispatch: redux.Dispatch<Store.All>): ConnectedDispatch => ({
  increment: (n: number) => {
    dispatch(incrementCounter(n))
  },
})

class CounterComponent extends React.Component<ConnectedState & ConnectedDispatch & OwnProps, OwnState> {
  // ...
}

export const Counter: React.ComponentClass<OwnProps> =
  connect(mapStateToProps, mapDispatchToProps)(CounterComponent)

TypeScript’s impact is typically minimal. Aside from the type annotations this is exactly what we would write in JavaScript. Given the global state:

  • mapStateToProps will extract relevant data into an object implementing the component-specific ConnectedState.

  • mapDispatchToProps will let us dispatch new actions to alter application state through an implementation of ConnectedDispatch.

  • connect will wrap CounterComponent and return a component implementing OwnProps

That’s exactly the same as in JavaScript. We export the wrapped component for use elsewhere in the application, where it will now have be able to access the redux store.

The Counter in Action

Let’s get to counting. All we need is a new store, a <Provider /> component, and a call to ReactDOM.render(). We’ll set it all up in src/index.tsx (our webpack entry point):

import * as ReactDOM from 'react-dom'
import { Provider } from 'react-redux'

import {
  reducers,
  Store,
} from './reducers'

import { Counter } from './components/counter'

let store: Redux.Store<Store.All> = Redux.createStore(reducers)

ReactDOM.render(
  <Provider store={store}>
    <Counter label='a counter' />
  </Provider>
, document.body)

And that’s it! Webpack the app, load dist/bundle.js in an HTML page, and things will be up and counting in no time.

In conclusion

Why all the ceremony?

We’ve added an awful lot of verbosity just to see it vanish from the compiled app. Type definitions and explicit signatures have ancillary value as supplemental documentation, but there are concrete benefits as well.

Let’s run a little experiment. We’ll remove the Counter component’s label in the finished app, just to see what happens.

<Provider store={store}>
  <Counter />
</Provider>

It’s easy enough to forget a prop when writing in JavaScript, but this sort of thing breeds runtime errors down the line. With static type-checking, however, TypeScript will refuse to compile the application with the label missing:

ERROR in ./src/index.tsx
(20,7): error TS2324: Property 'label' is missing in type
  'IntrinsicAttributes & IntrinsicClassAttributes<Component<OwnProps, {} | void>>
  & OwnProps & { chi...'.

Repeat the experiment with actions, stores, reducers–if we accidentally pass the wrong parameters through the application, TypeScript will let us know.

This isn’t to say that typing will do away with runtime errors entirely, though with proper setup it can help reduce their incidence. Even beyond the benefits of (somewhat limited) type-safety, the documentation we gain around the structure of application components will improve visibility and ease maintenance down the line. TypeScript is a tool, as are Flow and Dart. It’s no replacement for thoughtful development, but used appropriately they can all shine light into dark corners and provide a more pleasant development experience for everyone on the team.

And there you have it, the long story behind a counter demo. The completed project is available for reference on github, and I’m looking forward to your suggestions, experiences, and feedback over on twitter.

Happy dev-ing!



Getting Started with Redux and TypeScript

Redux is trending, and a number of applications I’ve reviewed recently are built around its central dogma of pure functions and unidirectional dataflow. These projects grow with mixed results, but the ones with the happiest teams attached share certain similarities in their clear delineation between state in actions and stores; their well-documented central store; and the obvious relationships they establish between the store and its reducers.

First, some context. Redux shares a common motivation (and a diminished-but-not-entirely-eliminated volume of boilerplate) with Facebook’s flux architecture, but centralizes application state and adopts a functional approach to managing it. As Redux applications grow, however, a central, organically-evolving store can grow into a big problem.

In a weakly-typed language like JavaScript, it’s not always obvious what certain variables represent. That’s true for even the simplest objects, but take a reasonably complex one–the monolithic store at the heart of a large-ish web application, say–and saddle up those gumshoes for some serious sleuthing.

You already have a large-ish redux application? No problem. Its reducers can be decomposed to specific concerns and gradually extracted to separate applications. But recalling the central role of clarity in developer happiness, the biggest problem may not be the size of the application but simply understanding the shape and provenance of the state inside it.

Over the past few years, the browser-side ecosystem has grown a host of projects focused on improved type-safety for frontend applications. From Flow annotations through typed supersets like TypeScript or entirely new languages like Dart, strong types no longer need to be the second-class citizens of the browser.

Let’s take a look at the patterns that emerge as we formalize the types inside a Redux application. We can somewhat arbitrarily start with TypeScript, but similar approaches carry over to Flow, Dart, or any other flavors of the week. JavaScript works. Redux works with JavaScript. We’re not adding anything except formality–but in the right setting, with visibility on the line, that’s worth something.

If you’re just interested in the results, check out the finished counter over on github. Otherwise, it’s time to get to work.

Setting up

It would hardly be a JavaScript project if it began without at least a few dependencies. We’ll need the typescript compiler, webpack and the ts-loader for bundling, and redux for the application itself.

$ npm install -g typescript webpack
$ npm install --save-dev redux ts-loader

There’s also some initialization, as we need to tell the typescript compiler about the project:

$ tsc --init
message TS6071: Successfully created a tsconfig.json file.

Manually edit tsconfig.json to enable JSX and include the root typings definition:

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "ES5"
  },
  "exclude": []
}

We’re almost ready to roll. All that’s left is to configure the webpack build. There’s a detailed guide on this over in the typescript handbook, but just use the following webpack.config.js:

module.exports = {
  entry: './src/index.ts',
  output: {
    filename: './dist/bundle.js',
  },
  resolve: {
    extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
  },

  module: {
    loaders: [{ test: /\.ts$/, loader: 'ts-loader' }]
  }
};

The Counter Application

Phew, setup. Take a quick water-break, catch your breath, and when you’re back we’ll implement a small application. Counters are simple. Let’s do a counter. Press a button, watch a number increase–it’s hardly the definition of excitement, but illustration’s sake it will do.

Note that the webpack configuration above had made a couple of assumptions that we’ll need to meet:

  1. Files with the .ts extension will be pushed through TypeScript
  2. The project will be compiled from the src directory

In other words, we’ll wind up with a directory structure like this:

├── dist
└── src
    ├── actions
    │   └── index.ts
    ├── index.ts
    └── reducers
        └── index.ts

Actions

In Redux, actions are events that can update application state. For the sake of simplicity, we can describe actions in terms of a generic interface with a type (what the action is) and a payload it might (or might not) be carrying:

interface Action<T, P> {
  type: T
  payload?: P
}

Next, we’ll add an action and a companion action creator to increment the counter.

type ActionTypes = 'INCREMENT_COUNTER'

type IncrementCounterPayload = { delta: number }
export type IncrementCounter = Action<ActionTypes, IncrementCounterPayload>

export const incrementCounter = (delta: number): IncrementCounter => ({
  type: 'INCREMENT_COUNTER',
  payload: { delta },
})

There are several patterns of interest here. Note how:

  • We’re using type (rather than interface) to close these actions for extension. They’re done. If many actions share slight variations in payload we might

  • Both the action creator and the action are exported. These have slightly different uses in mind, and–though we could extract each towards the modules (stores and middleware for the action, components for the action creator) where they’re used–keeping them together is convenient for both clarity and testing.

  • We tie the actions to a string literal type, even though our reducers can’t take direct advantage of it at this time. More on that in a moment, but it shouldn’t be the case for long: combined with TypeScript#9407, explicitly defining ActionTypes should ensure that we only reference known actions. Stay tuned.

Note also that the actions include more type information than we really need, which will be a recurring theme throughout this project. Even in cases when an anonymous declaration or type-inference could reduce a bit of boilerplate, full definitions may help improve legibility and intent. They’re not a hard requirement. If you’ve been here before and they’re not your style, just ignore them.


Now that we have something together, let’s pause for just a moment to see TypeScript in action.

Since this code eventually needs to run in a browser, and since browsers know nothing about the strange type annotations now littering our code, the annotations will stick around only long enough for the type-checker to sign off. Let’s compile the action:

$ tsc src/actions/index.ts

We can now find the JavaScript output in src/actions/index.js:

"use strict";
exports.incrementCounter = function (delta) { return ({
    type: 'INCREMENT_COUNTER',
    payload: { delta: delta }
}); };

Once our implementation has percolated through the compiler, it arrive back at JavaScript–and likely the same JavaScript we would have written if TypeScript weren’t in the mix at all. Everything added–types, interfaces, casts, and so on? That’s for our benefit.

But that’s just TypeScript. On to the store.

Store

This is why we’re here. Redux relies on a central store; leave it unattended, and the result trends towards an ominous black box. But by preemptively declaring what’s inside, we can avoid the mystery and enlist the type-checker in ensuring that it doesn’t creep back in.

Of course, the store behind our counter isn’t very complex:

export namespace Store {

  export type Counter = { value: number }

  export type All = {
    counter: Counter
  }
}

As with the actions, this structure belies a few deliberate decisions about the store. In particular, note that:

  • A single, finalized type–All in the example above–is exposed to describe the store’s complete structure. When a Redux application shares the store, it shares all of it. In cases where we need less state–the arguments to named reducers, for instance–we may only want a subset of the state. That’s fine; other types within the Store can be exposed as needed.

  • The Store occupies its own namespace, which could live in a top-level .d.ts declaration, or within a module somewhere near the reducers. Both work. It’s likely a good idea to import the Store while starting out to keep dependencies explicit, but as the application grows, it may be convenient to allow global, implicit references to the Store type.

  • All types in the store are declared within its namespace. Even though action payloads and component props may closely resemble data-structures that live within the store, as the application grows it will be clearer to define the store’s state separately from other, transient state within the application.

For all that, we haven’t really added anything new. We’ve just taken the data structures that would already exist inside a Redux application and laid out their shape in formal terms.

“But wait!” you’re thinking. “You haven’t even initialized the store! All we’ve got is a lousy type definition”. And you’re right: though it’s useful to look at its structure now, we won’t be able to implement the store until we’ve written a reducer.

Reducers

Now that we have actions and a store–or at least its shape–we can get ready to update it. Here’s a simple reducer that will consume the IncrementCounter action and update the counter accordingly:

import {
  Action,
  IncrementCounter,
} from '../actions'

const initialState: Store.Counter = {
  value: 0,
}

function counter (state: Store.Counter = initialState, action: Action<any, any>): Store.Counter {
  const { value } = state
  switch (action.type) {
    case 'INCREMENT_COUNTER':
      const { payload } = action as IncrementCounter
      const newValue = value + payload.delta
      return { value: newValue }
  }

  return state
}

This is standard Redux. Blink, and you could miss the modicum of type-safety that we’ve now sprinkled around the edges. We now expect the same type (Store.Counter) coming in and going out; we expect an Action with a specific format, and we refer to our IncrementCounter type when extracting the action’s payload. It’s also worth noting that:

  • Action type definitions can be imported on demand by the reducers that handle them

  • Reducers explicitly provide a default state for zeroing out the store to the appropriate type

  • We reference our app-specific Action to clarify inbound actions’ format. Since we’re defining our own actions, we can add some specificity about their shape (The default redux.Action definition is { type: any })

We’ve gained a bit of visibility into the interplay between actions and reducer, but the results aren’t entirely satisfying. In particular, the case statements aren’t checked against our specific action types: if we could restrict matching to specific actions (and enlist the type-checker in enforcement), accidental typos couldn’t slip into the action types. Something like this:

function counter (..., action: Increment | Decrement): Store.Counter {
  // ...
}

But alas. After wrestling with several different approaches to action matching in several very different projects, this is how things stand. We reassert the action‘s complete type after matching on its (string) .type, but as far as the reducer’s signature goes, it’s Action<any, any> for now.

Redux + TypeScript

In any case, we have a complete reducer and we’re finally ready to put it all together. Here’s how it looks:

import { createStore, store as ReduxStore } from 'redux'
import { reducers, Store } from './reducers'

const store: ReduxStore<Store.All> = createStore(reducers)

Ready to try it out? In index.ts, let’s set up a store and fire off some updates:

store.subscribe(() => {
  console.log(store.getState())
})

store.dispatch(incrementCounter(1)) // { counter: { value: 1 } }
store.dispatch(incrementCounter(1)) // { counter: { value: 2 } }
store.dispatch(incrementCounter(1)) // { counter: { value: 3 } }

It may not look like much, but from here on out the counter application will be working with statically-checked actions, reducers, and store. So far, we’ve seen structure and definitions of:

  • an application-specific Action generic, as well as a pattern for defining actions and their companion action creators that implement it

  • a shared Store containing the application state

  • a reducer consuming the actions to populate a portion of the store

Sound a bit like Redux?

To be continued

Next up, we’ll extend the counter application with a React frontend, applying the same types and patterns introduced here to produce a user-facing UI.

Note: this is the first part of a short series about bolstering Redux applications with TypeScript. In the next part, we extend the application with a simple, React-based UI. Read on!



But wait! There's more—

View all posts