TypeScript any vs unknown: Why any Turns Off the Type Checker
any is TypeScript's escape hatch: a value typed any can be used as anything, and the compiler stops checking it. unknown also accepts any value, but you have to prove what it is before you use it. Swapping any for unknown, or for a real type, is one of the cheapest ways to catch bugs before they ship.
The difference
- any: you can assign it anywhere, call it, and read any property. The compiler checks nothing, and any spreads to every value derived from it.
- unknown: you can assign anything to it, but you cannot use it until you narrow it with typeof, instanceof, a property check, or a type guard.
const a: any = JSON.parse('{"total": "12.50"}');
a.total.toFixed(2); // compiles, throws at runtime: toFixed is not a function
const u: unknown = JSON.parse('{"total": "12.50"}');
u.total; // compile error: 'u' is of type 'unknown'
Before: any at the boundary
async function getOrder(id: string): Promise<any> {
const res = await fetch(`/api/orders/${id}`);
return res.json();
}
After: unknown plus validation
For larger payloads, a schema library such as Zod or Valibot writes the type guard for you and gives clear error messages.
type Order = { id: string; total: number };
function isOrder(value: unknown): value is Order {
return (
typeof value === 'object' && value !== null &&
typeof (value as Order).id === 'string' &&
typeof (value as Order).total === 'number'
);
}
async function getOrder(id: string): Promise<Order> {
const res = await fetch(`/api/orders/${id}`);
const data: unknown = await res.json();
if (!isOrder(data)) throw new Error(`Unexpected order payload for ${id}`);
return data;
}
Safer alternatives to any
- unknown for values you have not checked yet (API responses, JSON.parse, catch variables)
- Generics when a function works with many types but must keep them consistent
- Union types when a value can be one of a few known shapes
- Record<string, T> for dictionaries instead of any objects
- A precise type from the library, or a small local type, instead of any for third-party data
Settings that help
- strict: true in tsconfig.json enables noImplicitAny, so parameters without types become errors
- useUnknownInCatchVariables (part of strict) types catch (err) as unknown instead of any
- The typescript-eslint rule @typescript-eslint/no-explicit-any reports every explicit any
- The no-unsafe-* rules (no-unsafe-member-access, no-unsafe-call) catch any leaking from libraries
Find every any in your repository
CodeInspector flags explicit any as rule TS0010 ("Explicit any type") with the file and line, alongside hardcoded secrets and other TypeScript findings. Scan a repository at https://codeinspector.app; new accounts get 100 free credits.