any vs unknown in TypeScript
Two ways to opt out of type checking — one unsafe, one that forces you to check first.
2 min read
any and unknown both represent "a value whose type isn't known ahead of time" — data from JSON.parse, a third-party library with no types, user input. They look similar, but any disables type checking entirely, while unknown keeps it active and forces you to check before doing anything with the value.
any: no checking at all
let data: any = fetchExternalData();
data.whatever.you.want(); // no error -- TypeScript trusts you completely
data.toFixed(2); // no error, even if data is actually a string
data(); // no error, even though it's not a functionOnce a value is typed any, TypeScript stops checking it — and worse, any spreads: assign an any value to another variable, pass it into a function, and that destination silently becomes untyped too, unless it has its own explicit annotation. This is why any is sometimes called a hole in the type system — a single any can undermine type safety well beyond the one variable it was assigned to.
unknown: checking required before use
let data: unknown = fetchExternalData();
data.whatever; // Object is of type 'unknown'
data.toFixed(2); // Object is of type 'unknown'unknown refuses to let you do anything with the value until you've narrowed it to a specific type — using the same narrowing techniques (typeof, instanceof, custom type guards) covered in an earlier lesson:
function process(data: unknown) {
if (typeof data === "string") {
console.log(data.toUpperCase()); // fine -- narrowed to string
} else if (typeof data === "number") {
console.log(data.toFixed(2)); // fine -- narrowed to number
}
}Where each is appropriate
unknown is the correct choice essentially anywhere you'd have reached for any in older TypeScript code — the result of JSON.parse(), a catch block's error variable (in modern TypeScript, catch (error) is typed unknown by default, precisely because a thrown value could genuinely be anything), or a function parameter accepting arbitrary external input. It gives you the same flexibility to accept any value, while still requiring you to prove what it actually is before using it.
any still has a narrow, legitimate use: gradually migrating a large plain-JavaScript codebase to TypeScript, where some values genuinely can't be typed yet and you need an escape hatch temporarily. Outside of that kind of migration, reaching for any is almost always a sign that unknown, a proper interface, or a generic would do the same job more safely.
The rule to remember
If you catch yourself typing any, ask whether you actually need to skip type checking, or just don't know the type yet. In the second, far more common case, unknown gives you the same starting flexibility without giving up the safety that's the entire reason to use TypeScript.
With the type system's sharper edges covered, the final section applies all of this to arrays, tuples, and classes in practice.