What 'any' Actually Does to Your Type System

'any' is not just 'I'll type this later.' It's 'I'm opting this value out of type checking.' The problem is that 'any' is contagious — a value typed as 'any' propagates its type unsafety to every place it's used. If you spread an 'any' object, the properties become 'any'. If you pass it to a function, the function's parameters can accept anything.

In a large codebase, a few 'any' values can hollow out the type safety of entire subsystems. I've audited codebases where 70% of the runtime errors were traceable to values that passed through at least one 'any' along the way.

What to Use Instead

For genuinely unknown types, 'unknown' is the right choice. Unlike 'any', 'unknown' forces you to narrow the type before using the value — a runtime check or a type guard. This is the safe version of 'I don't know what this is yet.'

For flexible function parameters, generic types are almost always better than 'any'. For external data (API responses, localStorage), zod or a similar runtime validation library gives you both runtime safety and inferred TypeScript types at the same time.

Key takeaways

  • Replace 'any' with 'unknown' for genuinely unknown types — unknown forces type narrowing before use, which surfaces the safety check you were skipping
  • For external data sources (APIs, user input), use zod or valibot for runtime validation — you get both a runtime check and an inferred TypeScript type for free
  • Enable 'noImplicitAny' in tsconfig to make TypeScript error on implicit any — this prevents the worst cases where types silently fall back to any

Conclusion

'any' is a debt, not a shortcut. Every 'any' in a codebase is a place where the type system has stopped working. The refactor to remove them is never as painful as the bugs you're preventing.

Enjoyed this article?

Vivek Kumar Singh

Vivek Kumar Singh

Technical Expert · Full Stack Cloud Engineer · Tokyo, Japan