Working with dynamic or external data in TypeScript can be tricky especially when you’re not sure what shape that data will take. This is where the any and unknown types come in, each handling uncertainty in a different way. While any acts as a “turn off type safety” switch, letting you do anything without checks, unknown forces you to validate and narrow types before using them.
Understanding the key differences between these two special types will help you write much safer, more predictable, and maintainable TypeScript code, especially when handling API responses or user input.
What is 'any' in TypeScript?
Example:
let value: any = "Hello";
value = 42;
value = { greet: "Hi" };
console.log(value.nonExistentMethod()); // No error at compile time, will crash at runtime!
What is 'unknown' in TypeScript?
let result: unknown = "Hello";
if (typeof result === "string") {
console.log(result.toUpperCase()); // ✅ Safe, type checked
}
Trying to use unknown directly triggers a compile-time error!
Type Narrowing for 'unknown' (typeof, instanceof, custom guards)
if (typeof input === "string") { /* ... */ }
if (input instanceof Array) { /* ... */ }
function isUser(val: unknown): val is User { return typeof val === "object" && val !== null && "email" in val; }
How TypeScript Treats Type Checking for 'any' vs 'unknown'
- any: No type checks; compiler lets anything through.
- unknown: The compiler requires type narrowing or assertion before use, enforcing safety.
Why 'unknown' is Safer than 'any'
When to Use 'unknown' Instead of 'any'
Use unknown for external data with unpredictable shape API responses, user input, deserialized JSON, so you’re required to validate before using it.
function handleApiResponse(data: unknown) {
if (typeof data === "object" && data !== null && "success" in data) {
// Safe to proceed!
}
}
When 'any' Is Necessary (Limiting Scope)
Common Mistakes Developers Make with 'any'
- Overusing any leads to silent bugs and runtime crashes.
- Assigning any too early disables TypeScript for the rest of the code.
- Using any for APIs, user data, or third-party packages instead of validating types.
Real-World Use Cases for 'unknown'
1- Handling API Response Data with 'unknown'
function getData(): Promise<unknown> { /* ... */ }
getData().then(response => {
if (typeof response === "object" && response !== null) { /*...*/ }
});
2- Working with Dynamic User Input Safely Using 'unknown'
function processInput(input: unknown) {
if (typeof input === "string") { /* ... */ }
}
3- Validating Unknown JSON Data
function validateJSON(val: unknown): val is User {
return typeof val === "object" && val !== null && "email" in val;
}
Migrating from 'any' to 'unknown': Step-by-Step
- Change your variable’s type from any to unknown.
- Add type checks wherever you use that variable.
- Use custom type guards as needed.
- Refactor dependent code to ensure strict safety.
TypeScript Best Practices: Avoid 'any', Embrace 'unknown'
- Default to unknown for untyped data.
- Only use any for prototyping, legacy code, or unavoidable hacks.
- Always narrow unknown to concrete types before use.
- Leverage custom guards for advanced validation.
Summary
The any type gives you total freedom but sacrifices all safety making TypeScript act like plain JavaScript, with all its potential runtime surprises. In contrast, unknown is a safer alternative, allowing you the flexibility to receive any value while ensuring you still leverage TypeScript’s powerful compile-time checks.
