TS any / unknown
Both any and unknown sit at the top of the type system — they can hold any value. They differ in what you can do with that value.
any — opt out of type checking
TS
let x: any = 1;
x = 'hello';
x = { foo: 'bar' };
x.toUpperCase(); // OK at compile, crashes if x is a number
x.foo.bar.baz(); // OK at compile, crashes at runtime
console.log(x);
Anything goes. The compiler stops helping you. Useful as an escape hatch; dangerous as a habit.
unknown — "I don't know yet"
TS
let u: unknown = 1;
// u.toFixed(2); // ✗ error — Object is of type 'unknown'
// u.foo; // ✗ error
if (typeof u === 'number') {
u.toFixed(2); // ✓ narrowed to number
}
You can assign anything to unknown, but you can't use it until you narrow.
The contagious nature of any
TS
const a: any = 1; const b = a.foo; // b: any const c = b.bar; // c: any — and so it spreads
One any tends to spread through every downstream variable. unknown stops at the boundary.
Real-world examples
| Use case | Pick |
|---|---|
| Parsed JSON before validation | unknown |
| Catch-clause variable | unknown (TS 4.0+ default) |
| Untyped third-party API | unknown + a type guard |
| "I really mean it, anything" | any — but justify it in a comment |
Strict-mode setting
Enable noImplicitAny (part of strict) and TS errors when it can't infer a type — forcing you to be explicit instead of silently falling back to any.
Tip: If you genuinely need an escape hatch, prefer
unknown + a narrowing check. any says "trust me"; unknown says "I'll prove it".Example
Example
let a: any = 1;
a = 'hello'; // anything goes — no safety
a.foo.bar.baz(); // type-checks but crashes at runtime
let u: unknown = 1;
// u.toFixed(2); // ✗ error — must narrow first
if (typeof u === 'number') {
console.log(u.toFixed(2)); // OK after narrowing
}
Try it Yourself »
Exercise
Type that holds anything but requires narrowing.
let x:
;
Seven letters.
Discussion
Loading…