"any is the off switch. unknown is the careful switch. never is the impossible."
Three types that don't behave like the others
Most types are about what a value is. any, unknown, and never are about what the type system itself does. They're meta-tools — escape hatches, safer escape hatches, and impossibility markers — and learning them well separates beginner TypeScript from professional TypeScript.
any — the off switch
any turns the type system off for the value it annotates. Property access doesn't check. Method calls don't check. Assignment to any other type doesn't check. The looseness is contagious — (anyValue).foo.bar.baz all stay any, and you've lost type safety for a whole subtree of access.
The legitimate uses of any are rare: interop with untyped JavaScript libraries (and even then, prefer writing a .d.ts declaration), one-off debugging, or migration of legacy code. In a strict codebase, every any should be flagged by a lint rule and require a comment justifying it.
unknown — the careful switch
unknown accepts any value (just like any) but won't let you do anything with it until you narrow. You can't call methods, can't access properties, can't assign it to a typed variable. You have to use typeof / instanceof / a custom type guard first.
The slogan: unknown is what any should have been. Same flexibility going in, much more safety going out. The Strict Mode track's useUnknownInCatchVariables flag makes catch (e) default to unknown exactly so this safety reaches your error handling.
never — the impossible
never is the empty type. No value is assignable to it. Its uses are very specific and very useful:
- Return type of a function that throws or loops forever — "this function does not return normally."
- The else branch in exhaustive narrowing — "this branch is unreachable, prove it."
- The result of conditional types that mean "this case doesn't apply" — you'll see this in the Type Manipulation track.
If a variable ever has type never when you weren't expecting it, that's the compiler telling you the type-flow has reached an impossible state. It's a signal, not a target.
any sits outside the type lattice — it's both a top and bottom that the compiler refuses to check. unknown is the true top type (everything is assignable to it). never is the bottom type (nothing is assignable to it, and it's assignable to everything). Once you see them as positions in the lattice, the behavior becomes predictable.Pippa's confession
: any, my first reaction is "why?" and my second is "can we make it unknown?" The number of legitimate anys in 30,000 lines of cwkPippa frontend is in the single digits — and each one has a comment explaining why narrowing wasn't possible. never shows up in exhaustive switches and in conditional-type returns. Both are deliberate. unknown is the default for any boundary I don't fully control — incoming JSON, third-party events, anything I parse before validating.