The unknown type is a type-safe counterpart to any. While any completely disables type checking, unknown represents a value that could be anything but forces you to perform type checking (like type guards, assertions, or instanceof) before you can operate on it.
In TypeScript, both any and unknown are top types, meaning they can hold any value. However, they serve fundamentally different purposes in terms of type safety. any is the escape hatch from the type system—it tells the compiler to completely opt out of type checking for that value, allowing any operation without restriction. unknown is the type-safe alternative: it also accepts any value, but it requires you to first prove the type through some form of narrowing (like typeof, instanceof, or a custom type guard) before you can perform operations on it.
Type Safety: any disables all type checking; unknown requires type narrowing before operations.
Property Access: any allows access to any property; unknown disallows property access entirely until the type is narrowed.
Function Calls: any allows being called as a function; unknown cannot be called without first proving it's a function.
Arithmetic: any can be used in arithmetic operations; unknown cannot.
Assignability: any can be assigned to any other type; unknown can only be assigned to any or unknown without a type assertion.
Spread in Objects: any can be spread into an object; unknown cannot be spread without prior validation.
Use unknown when: You receive values from truly dynamic sources (APIs, user input, JSON.parse) and need to validate them before use. This is the recommended practice for handling external data.
Use any when: You are migrating a JavaScript project to TypeScript incrementally and need a quick escape hatch, or when working with third-party libraries that are genuinely impossible to type. However, even then, prefer unknown with type guards.
Never use any as a shortcut to silence type errors. It defeats the purpose of using TypeScript. Prefer unknown or more specific types.
Use unknown in catch blocks: The caught error in a catch block is typed as unknown, forcing you to check its type (e.g., if (error instanceof Error)) before accessing error.message.
Use unknown in generic constraints: When you need to accept any type but still enforce type safety later, use T extends unknown (though this is rarely needed explicitly).