TypeScript is most effective when it is invisible. By leveraging type guards and exhaustive checks, we can make our systems impossible to represent in an invalid state.
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; size: number };
function area(s: Shape) {
switch (s.kind) {
case 'circle': return Math.PI * s.radius ** 2;
case 'square': return s.size ** 2;
default: {
const _exhaustive: never = s;
return _exhaustive;
}
}
}
This pattern ensures that when you add a new shape, the compiler forces you to handle it everywhere.