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.
| Type | Meaning | Primary use |
|---|---|---|
any | Opt out of type checking | Legacy or unsafe integrations |
unknown | Value exists, but its type is not known | External or untrusted data |
void | Function return value is not useful | Side-effect functions |
never | No possible value or normal return | Exhaustiveness 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 itThe 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 propagatedSenior-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:
anymeans “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:
unknownis the type-safe alternative toanywhen the type is genuinely unknown. Defaultcatchvariables tounknownwithuseUnknownInCatchVariables.
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:
voidis for side effects. Do not use it to mean “returns nothing” in the sense ofundefined— useundefinedwhen 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:
neveris 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 normallyunknown 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
anyopts out of the type system.unknownis the safe entry point for untrusted data.voidsignals ignored return values.nevermodels 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