Q5 of 38 · TypeScript

What is the difference between `any`, `unknown`, and `never` in TypeScript?

TypeScriptJuniortypescriptanyunknownnevertype-safetyfundamentals

Short answer

Short answer: `any` disables all type checking — unsafe escape hatch. `unknown` is the type-safe alternative: you must narrow it before operating on it. `never` represents a value that cannot exist — used for exhaustive checks and functions that never return. Prefer `unknown` over `any` when the type is genuinely unknown.

Detail

These three types occupy special positions in TypeScript's type hierarchy.

any: Opts a value out of type checking completely. Assignments to and from any always succeed. Accessing any property or calling it as a function is allowed. It is the escape hatch for gradual adoption or interoping with untyped JS, but it disables the protection TypeScript provides. A codebase with widespread any has TypeScript in name only.

unknown: The type-safe version of any. A value of type unknown can hold any value, but you must narrow the type before you can operate on it. This forces explicit handling of the unknown case. Use for parsed JSON, external API responses, and values from catch blocks (TypeScript 4+ defaults catch bindings to unknown).

never: The bottom type. A value of type never can never exist. Uses:

  • Exhaustive switch: if every case is handled, the default receives never
  • Functions that always throw or loop forever: return type is never
  • Conditional type inference: the "impossible" branch

TypeScript 4+ catch: The catch (err) binding is unknown by default with useUnknownInCatchVariables: true (enabled by strict). Always check err instanceof Error before accessing err.message.

// EXAMPLE

// any — unsafe
let x: any = "hello";
x.toFixed(2);  // no error! but will throw at runtime

// unknown — safe
let y: unknown = JSON.parse(rawJson);
// y.name;            // Error: Object is of type 'unknown'
if (typeof y === "object" && y !== null && "name" in y) {
  console.log((y as { name: string }).name); // narrowed
}

// never — exhaustive switch
type Color = "red" | "green" | "blue";
function getHex(c: Color): string {
  switch (c) {
    case "red":   return "#FF0000";
    case "green": return "#00FF00";
    case "blue":  return "#0000FF";
    default:
      const _exhaustive: never = c; // error if Color grows
      throw new Error(`Unknown: ${_exhaustive}`);
  }
}

// catch — TypeScript 4+ unknown
try {
  await page.goto(url);
} catch (err) {
  const msg = err instanceof Error ? err.message : String(err);
  console.error(msg);
}

// WHAT INTERVIEWERS LOOK FOR

The three-way distinction with practical use cases. `unknown` as the safe `any` — must narrow. `never` for exhaustive checks. The catch-binding change in TypeScript 4+. Strong candidates always reach for `unknown` over `any` by default.

// COMMON PITFALL

Using `any` 'just to make it compile' — this removes all type safety on that value and its downstream uses. Even a single `any` can propagate through a codebase through assignment.