Use `unknown` instead of `any` for values with uncertain types. Unlike `any`, `unknown` forces you to narrow the type before using it, keeping type safety intact.
Why This Matters
`unknown` is the type-safe counterpart to `any`. Both accept any value, but `unknown` requires you to narrow (check) the type before accessing properties or calling methods. This means bugs are caught at compile time instead of crashing in production. Switching from `any` to `unknown` typically costs 1-2 lines of narrowing code but prevents entire categories of runtime errors.
When you receive a value from an external source — an API response, a JSON parse, a catch block, a message event — you genuinely don't know its type. Both any and unknown can represent this uncertainty, but they behave very differently:
any: TypeScript says "I'll trust whatever you do with this value." No checks, no errors, no safety.
unknown: TypeScript says "You must prove what this value is before you use it." You get compile-time errors until you add type guards.
The difference is whether TypeScript protects you or abandons you. With unknown, you write a few extra lines of narrowing code. With any, you discover the bug in production.
The rule
Use unknown instead of any whenever a value's type is genuinely uncertain. Then narrow the type with typeof, instanceof, type guards, or Zod/schema validation before using it.
Bad example
// BAD: any lets you access anything without checkingasync function fetchUser(id: string): Promise<any> { const response = await fetch(`/api/users/${id}`); return response.json();}const user = await fetchUser("123");console.log(user.naem); // typo not caught — crashes silently
Good example
// GOOD: unknown forces you to validateinterface User { id: string; name: string; email: string;}function isUser(value: unknown): value is User { return ( typeof value === "object" && value !== null && "id" in value && "name" in value && "email" in value );}async function fetchUser(id: string): Promise<User> { const response = await fetch(`/api/users/${id}`); const data: unknown = await response.json(); if (!isUser(data)) { throw new Error("Invalid user response"); } return data; // fully typed as User}
How to detect
Search for any annotations that could be replaced with unknown: