A Letter of Appreciation

Kevin Welcher
4 min readApr 21, 2017

When developing, there are often small moments of bliss. You manage to un-tar a file on the first try without looking at the docs. You find an open source library and it is well thought out and maintained with love. You compose different technologies together and it creates a symphony. Stuff like that. I had one of those moments when upgrading from react-router@2.4.0. to react-router-dom@4.1.0 with Typescript.

First, I wanted to thank Michael Jackson, Ryan Florence, and all of react-routers wonderful contributors. You all have done a great service to the community building and maintaining such wonderful software.

Secondly, I wanted to thank Anders Hejlsberg and all the Typescript contributors. Typescript is the hero Javascript needs and deserves. I promise it wont burn down Gotham or something. It has been a true joy to work with and I can’t recommend it enough.

Lastly, and perhaps most obscurely, I wanted to thank Huy Nguyen and Tanguy Krotoff for writing the type definitions for react-router-dom. It is both of your brilliant contributions which sparked this post.

Thats nice… but why are you writing this?

Beyond the fact that these wonderful people have done such selfless work, they gave me an “ah-ha!” moment. At the confluence of upgrading react router and using typescript, I ran into a most brilliant juncture. Let’s see an example!

The moment

First, let’s write a simple router based app.

import {
BrowserRouter as Router,
Route
} from 'react-router-dom'
interface OneProps {}
const One: React.SFC<OneProps> = () => <h1>One</h1>
interface TwoProps {}
const Two: React.SFC<TwoProps> = () => <h1>Two</h1>
const SweetApp = () =>
<Router>
<div>
<Route path="/one" component={One} />
<Route path="/two" component={Two} />
</div>
</Router>

We have three functional components, two of which just renders headers and one which renders the router itself. Simple, no? But alas, typescript notifies us that there is an error!

Welp, that is a crazy error message. The Typescript compiler may not have the most elegant error messages in town, but they are nonetheless informative. Typically all you need to look at are the first lines and last two lines to figure out what is up. In this case: Type 'TwoProps' is not assignable to type 'RouteComponentProps<any>'.. The error message is telling me that the props I fed to Two are incorrect; it should be assignable to some type I have not encountered yet, RouteComponentProps. In short, the component we are giving to Route isn't what Typescript is expecting.

How can we fix it?

import {
BrowserRouter as Router,
Route,
RouteComponentProps
} from 'react-router-dom'
interface OneProps extends RouteComponentProps<any> {}
const One: React.SFC<OneProps> = () => <h1>One</h1>;
interface TwoProps extends RouteComponentProps<any> {}
const Two: React.SFC<TwoProps> = () => <h1>Two</h1>;
// ...

We import the missing type and extend our components props from it. Typescript stops complaining… but why did we have to do this [1]? To figure it out, we will look at the type of the component prop from Route.

interface RouteProps {
// ...
component?: React.SFC<RouteComponentProps<any> | void> | React.ComponentClass<RouteComponentProps<any> | void>;
// ...
}
class Route extends React.Component<RouteProps, void> {}

Ignoring the other properties or Route, we see that component accepts a stateless functional component (SFC) or a normal class based component. Both of these components accept props of RouteComponentProps<any> | void. Ah-ha! This is why Typescript was complaining. What is this type?

interface RouteComponentProps<P> {
match: match<P>;
location: H.Location;
history: H.History;
}

This is where my mind was blown. These three props are passed by default to any component rendered via a Route by react-router version 4. Do you see the beauty? Are your eyes literally replaced by hearts as you gaze at these joyous typed files?

😍 Types 😍

First, let me tell you about me. My name is Kevin, I am 💯 lazy. Ask my team, ask my PM. I use Typescript to protect others from me. It holds me accountable. It is a conversation between the (my beloved) compiler and myself. This conversation will be proclaimed, and ruthlessly enforced, with any unfortunate soul that touches my codebase. Solid picture? Great.

This is important to understand because I try to take any opportunity to force myself to behave. If I can write a type that makes sure I always check for nulls, or if the app is in a certain state, I will do it. A few extra characters is well worth the accountability.

Why bring this up? Because the authors of the type definitions for react-router did this for me. I can't help but imagine that they envisioned me, slouched low in my chair, writing half 🍑 code. In their minds they think "Ah, let's make sure Kevin does his job properly," and casually enforce that all components of Routes deal with the props passed down via the Router.

Two strangers have now forced me to write clean, well documented code. Future generations will look at these components and say “Ah yes, we can clearly see that we can access the location information from props. Bravo!"

You may not think this is anything worth noting, but to me… it was a joyous event. Thank you open source contributors, you make my coding experience jubilant.

❤️

[1] The any in interface OneProps extends RouteComponentProps<any> would be replaced by the path fragments of the route. If you had a route such as /name/:id, then it would be typed RouteComponentProps<{ id: string }>. Icing on the 🍰

--

--