TypeScript 5.5 shipped features that eliminate entire categories of bugs I've been writing defensive code around for years. Most people are sleeping on the inferred type predicate change. That one alone would have saved us 3 weeks of debugging in 2023.
In 2023, our payments team at a 400-person fintech was processing about $2M/day. We had a bug in production for 19 days before anyone noticed. The root cause: a type guard that lied. Someone wrote isValidTransaction(t: unknown): t is Transaction and the implementation was wrong. TypeScript trusted it completely. We lost $340k in failed reconciliations before Datadog caught the pattern.
TypeScript 5.5 would have made that class of bug significantly harder to write. Here's why.
This is the big one. Before 5.5, if you wrote a filter like items.filter(x => x !== null), TypeScript still typed the result as (string | null)[]. You'd add // @ts-ignore or write a manual type guard. Both are wrong. Both lie.
5.5 infers type predicates automatically. Write the filter. Get the right type. No ceremony.
const values = [1, null, 2, null, 3];
const numbers = values.filter(x => x !== null);
// numbers is now number[] not (number | null)[]
// TypeScript figured it outI've seen codebases with 40+ manual type guards that exist purely because of this limitation. Most of them are subtly wrong. Someone writes the guard once, the underlying type changes, nobody updates the guard, and now your types are a polite fiction.
Don't skip this one. The hasIndices flag on RegExp (/pattern/d) now correctly types match.indices. Before, you'd get undefined in the type even when you used the d flag. So people just cast it. as any everywhere in parsing code is how you get 2am PagerDuty alerts.
We had a log parser at Netflix that was doing exactly this. The as any cast hid a real off-by-one in index extraction for 6 months. Small thing. Real consequences.
Here's the thing about monorepos: they get slow. Specifically, TypeScript declaration emit gets slow because the compiler needs to understand your whole project to generate .d.ts files.
5.5's isolatedDeclarations option forces you to write explicit return types on exported functions. That sounds annoying. It is, initially. But it means each file can emit declarations independently, and tools like ESBuild and oxc can parallelize the work.
We moved a 600-package internal monorepo at a previous job to this pattern. Type-check times went from 4 minutes to under 40 seconds. CI bills dropped. Engineers stopped skipping type checks locally because they were too slow.
Don't do this halfway. If you turn on isolatedDeclarations, enforce it in your linter from day one. A codebase where half the exports have explicit types and half don't is worse than either extreme.
Small but surgical. If you write obj[key] and key is a constant string, TypeScript 5.5 now narrows the type through control flow. Sounds academic. It isn't.
const key = 'status' as const;
if (response[key] === 'active') {
// response[key] is now narrowed to 'active'
// before: it was still the union type
}Every API response parser I've ever reviewed has workarounds for this exact limitation. Now they don't need to.
5.5 isn't a paradigm shift. It's a collection of paper cuts finally getting addressed. Paper cuts are what actually kill you in production.