Lets Be Real: Its Time to Ditch `any` for `unknown` in TypeScript
Vatsal Trivedi

Vatsal Trivedi @trivedivatsal

About: Full Spectrum Developer • Integration Specialist • AI/ML Practitioner • Web • Frontend • UI • Graphics

Location:
Bangalore
Joined:
May 8, 2018

Lets Be Real: Its Time to Ditch `any` for `unknown` in TypeScript

Publish Date: Sep 22
34 17

We’ve all seen it in a pull request. A developer hits a snag with a tricky data type, and to get things working, they reach for the easiest tool available: any. It gets the job done and silences the compiler, but it comes at a hidden cost.

Our team has a Husky pre-commit hook set up to flag any, which is a great first step. But we all know that in a pinch, the --no-verify flag is an easy out. This makes the code review our most important line of defense. When you spot an any that has slipped through, it's the perfect opportunity to advocate for its safer, smarter alternative: unknown.


The Danger of any: A "Trust Me" Promise to the Compiler 🙈

Let’s be blunt: using any is like telling the TypeScript compiler to just look the other way. When you type a variable as any, you're effectively saying, "Disable all type-checking for this. I know what I’m doing."

This means you can do literally anything with that variable, and TypeScript won't stop you until it explodes at runtime.

Here's a classic example:

let myData: any;

myData = "This is a string";

// TypeScript has no problem with this line...
// but it will crash your app!
console.log(myData.toFixed(2)); 
// 😱 Uncaught TypeError: myData.toFixed is not a function

// The compiler is also perfectly happy with these obvious errors:
myData.someMethodThatDoesNotExist();
const result = myData * 10;
Enter fullscreen mode Exit fullscreen mode

Every any you use punches a hole in the type safety we rely on TypeScript for. It turns potential compile-time catches into frustrating runtime bugs and makes refactoring a dangerous guessing game.


The Savior: unknown to the Rescue! 🦸

This is where unknown comes in and saves the day. Just like any, you can assign any value—a string, a number, an object—to a variable typed as unknown.

So what’s the big deal? The crucial difference is that unknown won't let you do anything with the value until you first prove what it is. You are forced to handle the uncertainty before you can use it.

Think of an unknown variable like a locked box. You know something’s inside, but you have to check what it is before you can safely use it.

Let's fix the previous example with unknown:

let myData: unknown;

myData = "This is a string";

// The compiler immediately stops you. This is a good thing!
// ❌ Error: Object is of type 'unknown'.
console.log(myData.toFixed(2));

// Here's how you work with it safely:
if (typeof myData === 'number') {
  // OK, inside this block, TypeScript now knows myData is a number.
  console.log(myData.toFixed(2));
} else if (typeof myData === 'string') {
  // And in here, it knows it's a string.
  console.log(myData.toUpperCase());
}
Enter fullscreen mode Exit fullscreen mode

By switching to unknown, you’re prompted to write more defensive, robust code. It makes you handle different cases explicitly with type guards like typeof or instanceof, leading to a much more stable application.


The Code Reviewer's Playbook

Spotting an any during a code review is a great teaching moment. Here’s how to approach it constructively:

  1. Understand the "Why": The developer was likely just trying to solve a typing problem quickly. No need to be critical.
  2. Explain the Risk: Briefly mention that any bypasses type safety and can hide bugs that will only show up at runtime.
  3. Offer the Solution: Suggest replacing any with unknown. Then, guide them to add the necessary type check (e.g., an if block) to safely access the variable. This not only resolves the immediate risk but also makes the code's intent clearer for everyone.

The Bottom Line

While any is a tempting shortcut, it undermines the very reason we use TypeScript. unknown gives you the same flexibility to accept any type of value but without sacrificing type safety.

By encouraging the use of unknown in our code reviews, we're fostering a habit of writing more deliberate, predictable, and robust code. It forces us to confront uncertainty head-on, resulting in fewer bugs and a healthier codebase. So next time you're tempted to use any, pause and reach for unknown instead. Your future self—and your teammates—will be grateful.


Comments 17 total

  • Jess Lee
    Jess LeeSep 22, 2025

    Great first post!

  • Kyle Logue
    Kyle LogueSep 22, 2025

    This resonates with me so much! I've been burned by the any escape hatch more times than I care to admit, especially when dealing with API responses in contract-first development.

    One pattern I've found particularly useful is combining unknown with type predicates for API data validation:

    function isUserResponse(data: unknown): data is User {
      return typeof data === 'object' && 
             data !== null && 
             'id' in data && 
             'email' in data;
    }
    
    // Now you get full type safety
    if (isUserResponse(apiResponse)) {
      console.log(apiResponse.email); // TypeScript knows this is safe
    }
    
    Enter fullscreen mode Exit fullscreen mode

    The forcing function aspect you mentioned is spot-on - unknown makes you explicitly handle the uncertainty, which catches so many runtime issues during development rather than in production.

    Have you found any particular patterns or libraries that make the type guard approach more ergonomic? Always curious how other devs structure their validation workflows.

    • Vatsal Trivedi
      Vatsal TrivediSep 23, 2025

      That’s why I tie linting into the workflow with lint-staged + ESLint. It catches any (thanks to @typescript-eslint/no-explicit-any) before the commit even goes through.

      For runtime validation, I usually use JSON schemas when the data gets complex. The pre-commit guard keeps the codebase consistent and pushes us to use unknown with type guards or schemas instead of falling back to the easy escape hatch.

      • Kyle Logue
        Kyle LogueSep 23, 2025

        Are you using something like Husky for the pre-commit hook? I use lint-staged as well with a pre-commit hook that runs my package.json eslint . script

        • Vatsal Trivedi
          Vatsal TrivediSep 23, 2025

          Yes — I’ve set it up with Husky + lint-staged, running both Prettier and ESLint from the package.json scripts in the pre-commit hook. This way, code gets auto-formatted and linted before it’s committed.

  • Raziq Din
    Raziq DinSep 23, 2025

    Great Read!

  • JaRo
    JaRoSep 23, 2025

    While reading this I listen to this: https://youtu.be/h9VWxXaep-c?list=RDh9VWxXaep-c&t=714

    The future is uknown xD

    Nice article btw. Keep it up.

  • Oscar
    OscarSep 23, 2025

    I have no idea how I didn't realize that this existed... but I'm glad that I do now!

    • Vatsal Trivedi
      Vatsal TrivediSep 23, 2025

      Happens to all of us — glad you found it now!

  • leob
    leobSep 23, 2025

    Useful, really learned something today!

  • A Makenzi
    A MakenziSep 26, 2025

    any serves to make javascript and typescript interoperable. It's very useful when moving projects from js to ts. do not use it to disable the type system but removing it makes it hard to do smaller migrations or use untyped libraries

  • MarkAurit
    MarkAuritSep 26, 2025

    Being "Real" is _not _ introducing something that never should be in a production app in the first place as if it were a good idea. any and unknown should only be used in development situations where the type cannot at that time be known. Any workflow that allows any or unknown into production is flawed. Using unknown because its a better way to trick typescripts' Achilles Heel is not professional.
    The article itself is quite well done, bravo.

    • Vatsal Trivedi
      Vatsal TrivediSep 27, 2025

      It always comes down to choosing between taking on tech debt or doing the correct solution. Thanks for taking the time to read through!

Add comment