TypeScript that scales down
A common argument against strict TypeScript on small projects is that the ceremony isn't worth it — it's 'just a script,' or 'just a prototype,' so why pay the tax. I've stopped agreeing with that. Strictness scales down as well as it scales up; it just does a different job at each end.
On a large codebase, strict types stop one team from breaking another team's assumptions. On a three-file script, they do something smaller but still valuable: they stop you from breaking your own assumptions six weeks from now, when you've forgotten why a field can be null.
Here's the version of this I actually hit. A small internal tool for parsing CSV exports had a function like this:
function parseRow(row: string[]): { email: string; amount: number } {
return {
email: row[0],
amount: Number(row[1]),
};
}
With strict off, this compiles. With noUncheckedIndexedAccess on, row[0] is string | undefined, and the compiler asks the question I should have asked myself: what happens on a short row? The honest answer was that the app throws two function calls later, with a stack trace pointing nowhere useful. Fixing it at the source — validating row length, returning a Result-like type — took ten minutes and moved the failure to the place that could actually explain it.
The projects where I skip strict mode are the ones I regret coming back to. Not because the logic was hard, but because nothing in the code told me what I was allowed to assume. A quick script with loose types is fast to write and slow to trust later, by someone who is often me.
My default now: strict: true, noUncheckedIndexedAccess: true, on everything, from day one, including the disposable scripts. If a script survives past its first week, I'm glad the types are already there. If it doesn't, the extra minute of setup didn't cost me anything.