slowdev

We type-checked more than we needed to

For eighteen months I lived with a config in which every button was described by a string: primary, ghost, danger. It worked while there were three variants.

When there were seven, it turned out half the buttons were getting the wrong background shade — there's a typo somewhere in the markup and TypeScript said nothing. A string is a string.

So I sat down with types. One evening produced this:

  • a union of literals instead of a string;
  • a separate type for size, because size is not a colour variant;
  • satisfies on the styles object so keys are checked while values stay inferable;
  • dropping the string dictionary in the two places where it was hiding typos.

It got better. Then it got worse.

The button type sprawled across four files. To add an eighth variant I edit the union, the styles object, the list shown in the changelog, and something else in the tests. The compiler catches typos now, but I started adding new variants less often — simply because walking four files for one button is tedious.

In the end I kept the union and the key check, and collapsed the rest into one flat object with a comment. The check is still there; the ceremony is gone. Sometimes a type should catch a mistake, not describe an architecture.