DEV Community

Cover image for Why you should use TypeScript: a tale of self-documented code
Romain Guillemot
Romain Guillemot

Posted on • Updated on • Originally published at rocambille.github.io

Why you should use TypeScript: a tale of self-documented code

Make your code the best comment ever.

The undocumented array

Let's start with a simple line of JavaScript:

const a = [];
Enter fullscreen mode Exit fullscreen mode

Well... that's an array declaration: no big deal. What will be this array used for?

Hard to say without any context, plus the variable name isn't helping. Here is the context: I'm working on a side project using React and React Router (v6). Here is the real code, with a real variable name, in a file named routes.js:

const routes = [
  { path: '/foo', element: <Foo /> },
  { path: '/bar', element: <Bar /> }
];

export default routes;
Enter fullscreen mode Exit fullscreen mode

Here you are: the array will list the routes of the application. Then you may use it:

import { useRoutes } from "react-router";
import routes from './routes';

export default function App() {
  return useRoutes(routes);
}
Enter fullscreen mode Exit fullscreen mode

Of course, I didn't invent that pattern. I saw a similar code, and wanted to make it a template repository on GitHub. Some kind of personal React framework. Thus the array should be empty in routes.js. Indeed the template shouldn't declare any route: I don't know the application I will build from my framework.

const routes = [];

export default routes;
Enter fullscreen mode Exit fullscreen mode

I will remember how it should be filled since that's my own code... at least a while.

Eagleson's law

You may have heard it:

"Any code of your own that you haven't looked at for six or more months might as well have been written by someone else."

Sure I would be thankful to myself 6 months later — or next week — if I add some comment:

/* route format: {path: '/foo', element: <Foo />} */
const routes = [];

export default routes;
Enter fullscreen mode Exit fullscreen mode

It may looks ok at first glance, but looking closer it's not. Really not. First, I can't expect anyone — including me — to understand this comment. It describes something as a route format. It doesn't explain what to do with the routes array. In fact it's only useful if you already know what to do. Good enough for a reminder, but not what you would expect of a documentation.

And there is an other issue. Do you always read comments in code? I don't. Hours of code reading trained my eyes and my brain to ignore them as much as they can. I'm used to see comments as pollution between 2 lines of code.

Let's see the glass half full. Now we have a checklist to write useful documentation:

  • you should express it in a explicit, non-ambiguous form,
  • you should ensure it will be read.

"Explicit, non-ambiguous expression" doesn't look like a definition of writing, but of coding. You can't be sure any human will read what you wrote. But would you ask a program, you can always be sure it will "read" your code. So why not coding the documentation? A code version of this comment:

/* route format: {path: '/foo', element: <Foo />} */
Enter fullscreen mode Exit fullscreen mode

When the best comment is the code itself

That's where TypeScript can help us. In a nutshell, you can use TypeScript to describe the type of value expected for a variable:

const anyValue = '42'; // standard JS: will never complain
const anyString: string = '42'; // ok: '42' is a string
const anyNumber: number = '42'; // no: '42' is not a number
Enter fullscreen mode Exit fullscreen mode

That's for primitives. Would you need to ensure an object has specific, typed properties you can define an interface or a type alias:

interface MyInterface {
  anyString: string;
  anyNumber: number;
}

const myObject: MyInterface = {
  anyString: '42',
  anyNumber: '42', // again, wrong type !!!
};
Enter fullscreen mode Exit fullscreen mode

And that's what we need. Instead of a not-sure-to-be-useful comment, we can "type" the array. This will describe its future content:

import { ReactNode } from "react";

interface MyRoute {
  path: string;
  element: ReactNode;
}

const routes: MyRoute[] = [];

export default routes;
Enter fullscreen mode Exit fullscreen mode

TypeScript can't misunderstand this, and it will never forget to read it. You can try it with an invalid object:

import { ReactNode } from "react";

interface MyRoute {
  path: string;
  element: ReactNode;
}

const routes: MyRoute[] = [
  { whenToRender: '/foo', whatToRender: <Foo /> }
];

export default routes;
Enter fullscreen mode Exit fullscreen mode

This will output something like that:

Type '{ whenToRender: string; whatToRender: any; }' is not assignable to type 'MyRoute'
Object literal may only specify known properties, and 'whenToRender' does not exist in type 'MyRoute'
Enter fullscreen mode Exit fullscreen mode

That's how TypeScript can help you to self-document your code ;)

Top comments (1)

Collapse
 
jwp profile image
John Peters

Nice