Any & Unknown
When you must step outside the type system, do it safely.
TypeScript's type system is strict and powerful — it keeps your code safe, predictable, and bug-free. But sometimes, you genuinely don't know what type a value will be at compile time. Maybe it's data from an external API, user input, or a legacy library that doesn't have type definitions. For these situations, TypeScript provides two escape hatches: any and unknown.
Think of it like a strict Hyderabadi wedding. Every guest has a seating card — everyone knows exactly which table and which chair. That's TypeScript's normal type system. Every variable has a type, every function has a signature, everything is accounted for. But what happens when an uninvited guest shows up?
any is like letting that uninvited guest sit ANYWHERE, eat ANYTHING, do WHATEVER they want — total chaos, no rules, no supervision. Nobody checks them, nobody stops them. They might ruin the whole event, and nobody would even know until it's too late.
unknown is like letting the uninvited guest in, but keeping them at the security desk first — "You can come in, but first tell me EXACTLY who you are. Then I'll decide where you sit and what you can do." One is reckless, the other is cautious. Both let you handle the unexpected, but only one keeps you safe.
The any type is TypeScript's "I give up" button. When you declare a variable as any, you're telling the TypeScript compiler: "Stop checking this variable. I'll handle it myself." And just like that, all type safety disappears for that variable.
Watch what happens when you use any:
let data: any = "hello";
data = 42; // ✅ No error — any accepts anything
data = true; // ✅ No error — any accepts anything
data.toUpperCase(); // ✅ No error — but CRASH at runtime!
data.forEach(x => console.log(x)); // ✅ No error — CRASH!
Every single line compiles without errors. TypeScript doesn't complain at all. But toUpperCase() on the number 42 will crash at runtime. forEach on a boolean will crash at runtime. The compiler let it all through because you told it to trust you — but often you don't know what you're doing, and that's exactly when bugs happen.
Using any is like giving an RTC bus driver a blindfold — he can drive anywhere, but will he reach the destination? Maybe, maybe not. Probably a crash. You've essentially turned TypeScript back into JavaScript. You lose autocompletion, you lose type checking, you lose refactoring safety, you lose the entire reason TypeScript exists.
The worst part? any is contagious. If you assign an any variable to another variable, that variable also becomes any. The type unsafety spreads through your codebase like a virus. One any can silently infect dozens of other variables and functions, and you won't even realize it until things start breaking in production.
The unknown type is the safe version of any. It's the smarter, more responsible sibling. You can assign any value to an unknown variable — that part works just like any. But here's the critical difference: you cannot use an unknown value without first proving what type it is.
Watch what happens when you try to use unknown directly:
let data: unknown = "hello";
data.toUpperCase(); // ❌ ERROR! Object is of type 'unknown'
TypeScript blocks you. It says: "I don't know what this is, so I won't let you call methods on it." You MUST narrow the type first:
let data: unknown = "hello";
// Type narrowing with typeof
if (typeof data === "string") {
console.log(data.toUpperCase()); // ✅ Now it's safe!
}
// Type narrowing with instanceof
if (data instanceof Array) {
data.forEach(item => console.log(item)); // ✅ Safe!
}
// Type narrowing with Array.isArray
if (Array.isArray(data)) {
data.length; // ✅ Safe!
}
This process is called type narrowing — you start with a broad, unknown type and narrow it down to a specific type through checks. Only after proving the type does TypeScript allow you to use the value. This is the core mechanism that makes unknown safe.
There's also an important assignment rule: you CAN assign any value TO unknown (it accepts everything), but you CANNOT assign unknown TO other typed variables without narrowing:
let x: unknown = "hello";
let y: string = x; // ❌ ERROR! Type 'unknown' is not assignable
if (typeof x === "string") {
let y: string = x; // ✅ Now it works!
}
The analogy: unknown is like the security checkpost at HITEC City offices. You can enter the building, but the security guard will first ask — "Kaun ho? Kahan jaana hai? Kisko milna hai?" Until you answer those questions properly, you can't proceed past the lobby. That's exactly what type narrowing does — it forces you to prove who you are before you get access to the building's features.
The most dangerous trap in TypeScript is using any to silence compiler errors instead of actually fixing the type problem. It's the most common and most destructive pattern you'll see in TypeScript codebases. You get a type error, you slap : any on it, the error disappears, and you move on. But the bug is still there — it's just hiding, waiting to explode at runtime when your users are using the app.
// ❌ THE TRAP: Silencing errors with any
function processUser(data: any) {
return data.name.toUpperCase(); // No compile error, but...
}
processUser(null); // 💥 CRASH at runtime!
It's like putting tape over the "check engine" light in your auto-rickshaw — the light is off, but the engine is still broken. The problem hasn't gone away; you've just made it invisible. The right approach is to fix the actual type:
// ✅ THE FIX: Properly type the parameter
interface User { name: string; }
function processUser(data: User | null) {
if (data === null) return "";
return data.name.toUpperCase(); // Safe!
}
The second trap is thinking unknown can be used directly without narrowing. Developers sometimes assume unknown is just a fancier any and try to use it the same way:
let x: unknown = "hello";
console.log(x.toUpperCase()); // ❌ ERROR! Object is of type 'unknown'
You MUST narrow first with typeof, instanceof, or a type predicate. No shortcuts — this is the whole point of unknown.
The third trap is the implicit any trap. When you don't annotate a function parameter and noImplicitAny is turned off, TypeScript silently makes that parameter any. This is incredibly sneaky because you didn't even write any — but your code is just as unsafe:
// ⚠️ DANGER: 'data' is secretly 'any' if noImplicitAny is off
function process(data) {
return data.toUpperCase(); // No error if noImplicitAny is off!
}
// ✅ FIX: Always enable strict mode
// In tsconfig.json: "strict": true
function process(data: string) {
return data.toUpperCase(); // Properly typed!
}
Always enable strict: true in your tsconfig.json. It includes noImplicitAny and saves you from this silent killer. Think of strict: true as your Charminar — it stands strong and protects the entire city. Without it, things crumble quietly from the inside.
Let's put any and unknown side by side and see how they compare. This is your cheatsheet for deciding which escape hatch to use when you need one.
Comparison Table
| Feature | any | unknown |
|---|---|---|
| Can you assign any value to it? | ✅ Yes | ✅ Yes |
| Can you call methods on it directly? | ✅ Yes (but unsafe) | ❌ No (must narrow first) |
| Can you assign it to other typed variables? | ✅ Yes (but unsafe) | ❌ No (must narrow first) |
| Does it provide type safety? | ❌ No | ✅ Yes (after narrowing) |
| Autocomplete / IntelliSense? | ❌ No | ❌ No (until narrowed) |
| Risk of runtime errors? | 🔴 High | 🟢 Low |
When to Use any
- Almost NEVER in production code
- Quick migration from JavaScript to TypeScript (temporary!) — like scaffolding during building construction, remove it once the real structure is in place
- When you need to interact with a third-party library that has no type definitions and writing custom types would be impractical
- As a last resort when no other type works — but add a
// TODO: FIXMEcomment so you remember to fix it later
When to Use unknown
- When you genuinely don't know the type at write time — parsing JSON from an API, reading user input, handling dynamic data
- Function return types where the caller needs to check the type before using the value
- When building generic utility functions that need to accept any input but should force the consumer to validate before use
- When you want type safety but need the flexibility to accept any value as input
The Golden Rule
"If you must escape the type system, use unknown. Treat any like a poison — sometimes necessary in tiny doses, but usually deadly."
Think of it this way: any is like jumping into Hussain Sagar without knowing how to swim — you're on your own, no safety net, no lifeguard. unknown is like jumping in with a life jacket — you're still in the water, but you have protection. The life jacket is type narrowing — it keeps you afloat until you can prove you can swim.
Key Takeaways
- any turns off ALL type checking — it makes TypeScript act like JavaScript for that variable
- unknown accepts any value but forces you to narrow the type before using it
- Type narrowing means proving the type with typeof, instanceof, or Array.isArray before you can use the value
- Never use any to silence compiler errors — fix the actual type problem instead
- Always enable strict: true in tsconfig.json to prevent implicit any
- Golden rule: If you must escape the type system, use unknown. Treat any like poison.
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login