any switches the compiler off for that value and everything derived from it. unknown accepts anything and permits nothing until it has been narrowed.
function parse(raw: string): unknown {
return JSON.parse(raw);
}
const data = parse(body);
data.total; // error: object is of type 'unknown'
if (isOrder(data)) {
data.total; // fine, narrowed by the type guard
}
function isOrder(v: unknown): v is Order {
return typeof v === 'object' && v !== null && 'total' in v;
}
JSON.parse returns any, which means every value read from an API is untyped and the compiler agrees with whatever you claim about it — that is where most TypeScript runtime errors come from. Wrapping the boundary to return unknown forces a guard, and the guard is the only place a lie can be told. Writing those guards by hand is tedious enough that a schema library is usually the better answer, and either beats any.