Dataslope logoDataslope

Any, Unknown, Never

The escape hatch, the safe top type, and the empty bottom type

TypeScript's type system has three "special" types that sit at the edges of the type lattice: any, unknown, and never. They're often misunderstood, but they're essential for expressing concepts like "opt-out of type checking" (any), "accept anything but force narrowing" (unknown), and "this code is unreachable" (never). A fourth type, void, is also worth understanding in relation to these three.

This chapter answers: What is any and when should you (not) use it? How is unknown different? What does never represent? How do these types relate to each other?

The type hierarchy: top and bottom

Imagine the type system as a lattice. At the very top sits a type that accepts every value — the top type. At the very bottom sits a type that accepts no values — the bottom type. Every other type falls somewhere in between.

  • unknown is the true top type: every value is assignable to unknown.
  • never is the bottom type: no value is assignable to never, and never is assignable to every type.
  • any is the escape hatch: it bypasses the type system entirely.

any: the escape hatch

The type any disables type checking. A value of type any can be assigned to anything, and anything can be assigned to any. You can call any method, access any property, and the compiler won't complain:

Code Block
TypeScript 5.7

When is any acceptable?

  • During migration from JavaScript to TypeScript, when you don't yet have time to type everything.
  • When working with truly dynamic data (e.g., JSON from an external API you don't control, where the shape is unknown).
  • When calling legacy JavaScript libraries without type definitions.

When is any dangerous?

  • Everywhere else. It's a trapdoor: once a value becomes any, the compiler loses all knowledge of its type. Bugs slip through.
Code Block
TypeScript 5.7

The function double compiles without errors, but it produces nonsense at runtime when passed non-numbers. This is exactly what TypeScript is supposed to prevent. Avoid any whenever possible.

unknown: the safe top type

The type unknown is the type-safe alternative to any. Like any, it accepts every value — but unlike any, you can't do anything with an unknown value until you narrow it:

Code Block
TypeScript 5.7

To use an unknown value, you must prove its type via narrowing:

Code Block
TypeScript 5.7

What bugs does this prevent? It forces you to handle the "I don't know what this is" case explicitly. You can't accidentally call a method that doesn't exist. This is far safer than any.

When to use unknown:

  • When you truly don't know the type of a value (e.g., parsing JSON, reading from an external API).
  • As the parameter type for generic utilities that accept any value but require narrowing before use.

Compare any vs unknown side by side:

Code Block
TypeScript 5.7

The safeUnknown function is longer, but it's correct — it handles all cases without crashing.

never: the empty type

The type never represents the empty set — a type with no values. No value is assignable to never (except never itself). Where does this come up?

Functions that never return

These functions throw or loop forever, so they don't have a return value at all:

Code Block
TypeScript 5.7

Impossible branches in control flow

Code Block
TypeScript 5.7

In the default branch, shape is type never — the compiler has eliminated all possible cases, so no value can reach here. If you add a new variant (say, { kind: "triangle" }), the default line will error, forcing you to handle it.

Intersection of disjoint types

Code Block
TypeScript 5.7

Why is never useful? It lets you express "this code is unreachable" or "this type is impossible" in a way the compiler understands. When you see never, it's a signal that the compiler has proven something can't happen.

void: the "no useful return value" type

The type void means "this function is called for its side effects, not its return value." It's not quite the same as undefined (though functions that return void typically return undefined at runtime):

Code Block
TypeScript 5.7

void is a signal to readers (and the compiler) that the function's return value doesn't matter. You can assign the result to a variable, but the compiler won't let you use it in a meaningful way:

Code Block
TypeScript 5.7

void is the conventional return type for event handlers, loggers, and other side-effect functions.

void vs undefined

There's a subtle distinction:

  • void means "I don't care about the return value."
  • undefined means "the return value is explicitly undefined."
Code Block
TypeScript 5.7

In practice, void is more common. Use undefined when the return type matters (e.g., a function that might return T | undefined to signal absence).

Type relationships: assignability

Here's how the four types relate in terms of assignability:

  • any bypasses the type system: you can assign any to anything, and anything to any.
  • unknown is the top type: you can assign anything to unknown, but you can't assign unknown to anything (without narrowing).
  • never is the bottom type: you can't assign anything to never, but you can assign never to anything (this is useful for exhaustiveness checking).
  • void is a return-type-only marker: you can assign undefined to void, but not vice versa.
Code Block
TypeScript 5.7

Practice: a safe JSON parser

Let's solidify these concepts with a challenge. You'll write a function that parses JSON and uses unknown to ensure type safety.

Challenge
TypeScript 5.7
Type-Safe JSON Parser

Implement parseJSON(text: string): unknown that wraps JSON.parse and returns unknown (not any). Then implement getString(value: unknown): string that returns the value if it's a string, or throws an error otherwise. Test with valid and invalid inputs.

When to use each type

Use any when:

  • You're migrating JavaScript to TypeScript and need a temporary escape hatch.
  • You're interacting with truly dynamic code (e.g., a library without types).
  • You understand the risks and have a plan to narrow or refine later.

Use unknown when:

  • You don't know the type at compile time (e.g., parsing JSON, reading user input).
  • You want to force yourself (or consumers) to narrow before use.
  • You're writing generic utilities that accept any value.

Use never when:

  • A function never returns (throws or loops forever).
  • You want exhaustiveness checking in a switch or if/else chain.
  • You're expressing "this type is uninhabited" (e.g., intersection of disjoint types).

Use void when:

  • A function is called for side effects, not its return value.
  • You're defining callback signatures (e.g., event handlers).

Check your understanding

QuestionSelect one

What is the key difference between any and unknown?

unknown is deprecated, use any

any is type-safe, unknown is not

any bypasses type checking, unknown forces narrowing

They are identical

QuestionSelect one

What does the never type represent?

A function that returns nothing (void)

A value that is null or undefined

A type with no values (the empty set)

A value that is both string and number

Summary

any is the escape hatch — it disables type checking entirely. Use it sparingly, and only when you have no better option. unknown is the type-safe top type — it accepts any value but forces you to narrow before use. never is the bottom type — it represents impossible values, unreachable code, and exhaustiveness checks. void is a return-type marker for side-effect functions.

Together, these four types let you express the full range of type constraints: "accept anything" (unknown), "accept nothing" (never), "don't care about the return" (void), and "opt-out of type checking" (any). Mastering them is essential for writing precise, safe TypeScript code.

This concludes the Core Type System chapter. You've now seen primitives, arrays, tuples, objects, interfaces, type aliases, literals, functions, unions, intersections, narrowing, and the special types any/unknown/never/void. In the next chapters, we'll build on this foundation to explore generics, advanced type patterns, and type-driven architecture.

On this page