Topics
v5.0.0
On this page · sections

any vs unknown vs never vs void

These four TypeScript types solve very different problems. Confusing them is one of the most common sources of runtime bugs in production frontends.

TypeMeaningPrimary use
anyOpt out of type checkingLegacy or unsafe integrations
unknownValue exists, but its type is not knownExternal or untrusted data
voidFunction return value is not usefulSide-effect functions
neverNo possible value or normal returnExhaustiveness and impossible paths

any

Definition

any disables TypeScript’s static type checking for a value. The compiler stops tracking it.

let value: any = "hello"; value.foo.bar(); // ✅ Compiler allows it value.toFixed(); // ✅ Compiler allows it

The code may still fail at runtime. Worse, any is contagious — it spreads through your codebase silently.

function wrap(x: any) { return x; // return type is also any } const result = wrap("hello"); result.nonexistent(); // ✅ no error — any propagated

Senior-level usage

Avoid any when the type is merely unknown. Use it only when you intentionally accept losing type safety, typically at legacy or poorly typed boundaries.

function legacyAdapter(data: any) { return data.result; }

Interview: any means “trust me” — TypeScript stops protecting this value. It disables exhaustiveness checks and hides refactors.

unknown

Definition

unknown represents a value whose type is not known yet. Unlike any, TypeScript requires you to narrow it before using type-specific operations.

function processValue(value: unknown) { if (typeof value === "string") { return value.toUpperCase(); } if (typeof value === "number") { return value.toFixed(2); } }

Real-world example

External data should generally enter your application as unknown:

async function getUser(): Promise<unknown> { const response = await fetch("/api/user"); return response.json(); }

Then validate or narrow before using it as a User:

type User = { id: string; name: string }; function isUser(value: unknown): value is User { return ( typeof value === "object" && value !== null && "id" in value && "name" in value ); } const data = await getUser(); if (isUser(data)) { console.log(data.name); // data is User }

unknown vs any

const a: any = "hello"; a.toFixed(); // ✅ compiles — runtime error const b: unknown = "hello"; b.toFixed(); // ❌ Object is of type 'unknown'

unknown is assignable to anything (after narrowing), but nothing assignable to unknown is automatically usable:

const x: unknown = "hello"; const s: string = x; // ❌ must narrow first if (typeof x === "string") { const s: string = x; // ✅ }

Interview: unknown is the type-safe alternative to any when the type is genuinely unknown. Default catch variables to unknown with useUnknownInCatchVariables.

void

Definition

void indicates that a function’s return value is not intended to be used.

function logAnalytics(event: string): void { console.log(`Tracking: ${event}`); }

Typical frontend example:

const handleClick = (): void => { saveForm(); };

The function performs a side effect; the caller does not need a result.

Important distinction

void does not mean “the function can never return.” It means the caller should not depend on its return value.

function log(): void { console.log("done"); return undefined; // ✅ allowed — void is about caller intent }

Compare with undefined as an explicit return type:

function getValue(): undefined { return undefined; }

void describes function intent; undefined is an actual value type.

void in callbacks

type ClickHandler = () => void; // A function returning a value is assignable to () => void const handler: ClickHandler = () => { return 42; // ✅ return value is ignored by the type contract };

Interview: void is for side effects. Do not use it to mean “returns nothing” in the sense of undefined — use undefined when the value matters.

never

Definition

never represents an impossible value or a code path that cannot complete normally. It is the bottom type — assignable to every type, but nothing assignable to it except never itself.

1. Function that always throws

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

Since the function never successfully returns, its return type is never.

2. Infinite loops

function loop(): never { while (true) { // ... } }

3. Exhaustive checking

This is one of the most valuable uses of never in production TypeScript.

type Action = | { type: "add"; value: number } | { type: "remove"; id: string }; function reducer(action: Action) { switch (action.type) { case "add": return action.value; case "remove": return action.id; default: { const exhaustiveCheck: never = action; return exhaustiveCheck; } } }

If someone later adds { type: "reset" }, the compiler flags the unhandled case at compile time.

Interview: never is the bottom type — useful for impossible states, throwing functions, and exhaustive union checks.

void vs never

Definition

This is a common senior-level follow-up.

function log(): void { console.log("done"); }

The function finishes normally but has no useful result.

function fail(): never { throw new Error("failed"); }

The function cannot finish normally.

void → finishes, but result is not useful never → does not finish normally

unknown vs never

These represent opposite extremes of the TypeScript type system:

unknown → could be any value (top of the usable hierarchy) never → could be no value (bottom type)

After exhaustive narrowing:

function handle(value: string | number) { if (typeof value === "string") { return value.length; } if (typeof value === "number") { return value.toFixed(); } // value is never here — all union members handled const _impossible: never = value; return _impossible; }

Senior Interview Summary

What to say

any opts out of the type system. unknown is the safe entry point for untrusted data. void signals ignored return values. never models impossible paths and powers exhaustiveness.

Best mental model

any → "Do not check this." unknown → "Check this before using it." void → "No useful result." never → "No possible result."

Practical rule

External data → unknown Unsafe legacy code → any (quarantine it) Side-effect function → void Impossible path → never catch (e) → unknown (with strict flag)

Common pitfalls

any in catch blocks → use unknown + instanceof Error void vs undefined return → void = intent, undefined = value Skipping never default → miss new union members at compile time

Last updated on