Articles

TypeScript unknown vs any: What's the Difference

TypeScript's unknown forces a type check before use; any opts out of type checking entirely. When to reach for each in real code.

Takina Takina · · 4 min read
Dark-themed code editor showing TypeScript

TypeScript’s unknown and any both mean “this value could be anything,” but they behave almost oppositely once you try to use the value. any disables type checking on everything it touches, while unknown requires you to narrow the type before you can do anything with it. Reaching for any out of habit is one of the most common ways a TypeScript codebase quietly loses its safety guarantees.

What any actually does

any is an escape hatch. Once a value is typed any, TypeScript stops checking it — and, worse, stops checking anything derived from it. You can call methods that don’t exist, pass it where a completely different type is expected, and access arbitrary properties, all without a single compiler error:

function process(input: any) {
  input.toUpperCase(); // no error, even if input is a number
  input.whatever.deeply.nested; // still no error
  const n: number = input; // no error, even if input is a string
}

The problem isn’t just that any is unchecked — it’s contagious. Any value assigned from an any-typed expression becomes any itself, silently widening the blast radius of a single loosely-typed value through the rest of the function.

What unknown actually does

unknown also accepts any value, but the compiler refuses to let you do anything with it until you’ve proven what it actually is:

function process(input: unknown) {
  input.toUpperCase(); // compiler error: input is unknown
  if (typeof input === "string") {
    input.toUpperCase(); // fine — narrowed to string
  }
}

You’re forced to narrow with typeof, instanceof, a custom type guard, or a schema-validation library before the value becomes usable. This is the entire point: unknown says “I don’t know the type yet,” while any says “don’t bother checking.”

Side by side

anyunknown
Accepts any valueYesYes
Requires narrowing before useNoYes
Property/method access without a checkAllowedCompiler error
Assignable to other specific types directlyYesNo — requires a narrowing check first
Propagates looseness to derived valuesYesNo
Best forLegacy migration, deliberate opt-outExternal input: API responses, JSON.parse, catch blocks

Where each one shows up in practice

unknown is the correct type for anything arriving from outside your program’s control: the result of JSON.parse(), a fetch response body before validation, or a caught exception in a catch block (which TypeScript types as unknown by default in modern configurations, precisely to stop you from assuming its shape). In each case you don’t actually know the type yet, and unknown makes the compiler hold you to proving it before use.

try {
  riskyOperation();
} catch (err: unknown) {
  if (err instanceof Error) {
    console.log(err.message); // safe — narrowed
  }
}

any still has legitimate uses, but they’re narrower than most codebases treat them: incrementally migrating a JavaScript file where full typing isn’t worth the effort yet, or interfacing with a poorly-typed third-party library where writing an accurate type would take longer than the value it provides. Even then, it’s worth scoping the any as tightly as possible rather than letting it spread through a function’s return type.

Narrowing unknown without a runtime library

Type guards are the idiomatic way to narrow unknown into something usable:

function isUser(value: unknown): value is { id: string; name: string } {
  return (
    typeof value === "object" &&
    value !== null &&
    "id" in value &&
    "name" in value
  );
}

function greet(input: unknown) {
  if (isUser(input)) {
    console.log(`Hello, ${input.name}`); // narrowed
  }
}

For anything more complex — nested objects, arrays of records, optional fields — a schema-validation library that both validates at runtime and infers the TypeScript type from the same schema is generally less error-prone than hand-written guards, since the check and the type can’t drift apart.

Why this matters for real bugs

The difference isn’t academic. any is how a null from an API response ends up three function calls deep before it crashes at runtime with no compiler warning along the way — the type system simply stopped watching the moment the value was labeled any. unknown converts that same class of bug into a compile-time error: you cannot call .toUpperCase() on an unknown without TypeScript stopping you first, which pushes the “what if this isn’t a string” question to the point where the data first enters your program instead of wherever it happens to blow up. If you’re just getting oriented with the language, see getting started with TypeScript for the fundamentals this builds on, and TypeScript generics and utility types for how to keep types precise elsewhere in a codebase. The satisfies operator solves an adjacent problem — validating a value’s shape without widening its inferred type.

The takeaway

any turns off type checking; unknown keeps it on but demands proof before letting you use the value. Default to unknown for anything whose shape you don’t control yet — API responses, parsed JSON, caught errors — and narrow it with a type guard before use. Reserve any for the rare cases where an accurate type genuinely isn’t worth writing, and keep its scope as small as possible so its looseness doesn’t spread.

Takina Takina · · 4 min read

TypeScript Abstract Classes, Explained

Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.

#TypeScript #JavaScript #Web Development
Takina Takina · · 3 min read

TypeScript's never Type, Explained

never represents values that can't exist — it marks unreachable code, exhaustive switches, and functions that always throw or loop forever.

#TypeScript #JavaScript #Web Development
Takina Takina · · 4 min read

TypeScript readonly Modifiers Explained

TypeScript's readonly keyword blocks reassignment at compile time for properties, arrays, and tuples — with no runtime enforcement at all.

#TypeScript #JavaScript #Web Development