RizTech Academy logo
RizTech Academy
The Basic TypesLesson 4 of 625 min

any, unknown and never

Three special types sit at the edges of TypeScript's system: any turns type checking off, unknown is its safe replacement, and never is the type of a value that cannot exist. Understanding all three — and specifically preferring unknown over any — is one of the clearest dividing lines between beginner and competent TypeScript.

any — the off switch

any means "stop checking this — I take responsibility." A value typed any can do anything, be assigned anywhere, and the compiler says nothing:

let x: any = "Pune";
x.toFixed();          // no error — even though a string has no toFixed
x = 42;               // no error
x = { foo: true };    // no error
x.foo.bar.baz;        // no error — TypeScript has given up on x entirely
const n: number = x;  // no error — any flows into anything

Every line there is a potential runtime crash, and TypeScript flags none of them. any is a hole in the type system: once a value is any, all the guarantees you came to TypeScript for are gone for that value — and worse, any is contagious. It spreads: const n: number = x makes n a number that might actually hold anything, and the unsafety leaks onward.

This is why a codebase full of any gets almost none of TypeScript's benefit — it is JavaScript with extra syntax. The instinct to sprinkle any to make an error message disappear is the single most common way beginners neuter the tool.

unknown — the safe alternative

unknown is any's responsible cousin. It also holds any value — but unlike any, it will not let you do anything with that value until you have proven what it is:

let x: unknown = "Pune";
x.toFixed();          // error: 'x' is of type 'unknown'
const n: number = x;  // error: Type 'unknown' is not assignable to type 'number'

if (typeof x === "string") {
  x.toUpperCase();    // fine! inside this block, TypeScript knows x is a string
}

unknown says "this could be anything, so you must check before you use it." You cannot call a method, access a property, or assign it to a specific type until you have narrowed it (with typeof, a check, a type guard — the narrowing module). This is exactly the safety you want for values whose type you genuinely do not know at compile time — and it is why unknown is almost always the right choice where you were tempted to write any.

The classic use is a value from outside your program:

const data: unknown = JSON.parse(input);   // JSON.parse could return anything
// data.name;   // error — you must check the shape first
if (typeof data === "object" && data !== null && "name" in data) {
  // now you can safely work with data.name
}

JSON.parse returns any by default (a historical wart), but the moment you assign it to unknown, TypeScript forces you to validate before trusting it. Prefer unknown for external data, and let the compiler make you check.

any versus unknown — the rule

Both accept any value. The difference is what happens next:

  • any — you can do anything with it, no checks, no safety. It turns checking off.
  • unknown — you can do nothing with it until you prove its type. It keeps checking on.

The rule: never reach for any when unknown will do — which is almost always. any says "I give up on types here"; unknown says "I do not know the type yet, so make me check." The second is safe; the first is a hole. When you are genuinely tempted by any, ask whether unknown plus a check would work — it usually does, and it keeps the guarantee.

never — the impossible type

never is the type of a value that can never occur. It sounds abstract, and it has two very practical uses.

A function that never returns — because it always throws, or loops forever — returns never:

function fail(message: string): never {
  throw new Error(message);      // never returns normally
}

The type says "this function does not produce a value; it ends the program flow." That is more honest than void (which means "returns nothing useful") — never means "does not return at all".

Exhaustiveness checking — the genuinely important use, which the unions module builds on fully. When you handle every case of a union, the "impossible remaining case" has type never, and you can make TypeScript prove you handled everything:

type Status = "pending" | "paid" | "shipped";

function label(status: Status): string {
  switch (status) {
    case "pending": return "Pending";
    case "paid":    return "Paid";
    case "shipped": return "Shipped";
    default:
      const exhaustive: never = status;   // if a case is missing, status is not `never` -> compile error
      return exhaustive;
  }
}

If you later add "delivered" to Status and forget to handle it, that const exhaustive: never = status line fails to compile, because status could now be "delivered", which is not never. The compiler walks you to the missing case. This is one of TypeScript's best safety features, and never is what makes it work — you will see it properly in the exhaustiveness lesson.

Check your work

What any does. Turns off type checking for a value — it can do anything, assign anywhere, with no errors.

Why any is dangerous, and contagious. Every unsafe operation on it is unchecked, and it spreads its unsafety to whatever it is assigned to.

What unknown does. Holds any value, but permits nothing until you prove its type by narrowing.

The rule for any versus unknown. Prefer unknown — it keeps checking on; reach for any only in rare, deliberate cases.

The classic use of unknown. External data (JSON.parse, API responses), which you must validate before trusting.

What never is. The type of a value that can never occur.

never versus void. never means the function does not return at all (throws/loops); void means it returns nothing useful.

never's important use. Exhaustiveness checking — assigning the "impossible" case to never makes the compiler prove every union case is handled.

Practice

  1. Type a value any and do five unsafe things to it (call a wrong method, access nested nonsense, assign it to a number). Confirm TypeScript complains about none of them.
  2. Change it to unknown and try the same five things. Read each error.
  3. Narrow the unknown with typeof x === "string" and confirm you can use it inside the block.
  4. Assign JSON.parse(...) to unknown and confirm you must check its shape before accessing a property.
  5. Write a fail(message): never that throws, and confirm the return type is never.
  6. Build the label function with the never exhaustiveness check. Add a new status to the union and watch the default branch fail to compile. Handle it to fix it.
  7. Explain, in one sentence each, when you would use any, unknown, and never.

Official documentation

Next: inference — when to annotate, and when to let TypeScript do it.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship