Any vs Unknown in TypeScript

Any and unknown both accept every type, but any disables type checking while unknown forces you to narrow the type first. Learn the critical difference and when to use each.

7 min read

Both any and unknown in TypeScript can hold any value: a string, a number, an object, an array, anything. The difference is what you can do with that value afterward.

any turns off type checking completely, so the compiler lets you call anything on it. unknown keeps type checking on and forces you to prove what the value is before you use it.

Here is the simplest demonstration of the difference:

typescripttypescript
let a: any = "hello";
let b: unknown = "hello";
 
a.toUpperCase(); // ok: no checking
b.toUpperCase(); // Error

The first line compiles because any silences the compiler entirely, even though calling a method on the wrong runtime value would still crash. The second line fails to compile because unknown refuses to let you call a string method until you prove the value is actually a string.

texttext
Object is of type 'unknown'.

What Any Does

any is an opt-out from the type system. When you assign a value to an any variable or mark a parameter as any, TypeScript stops checking that code path entirely.

typescripttypescript
let value: any = "hello";
 
value = 42;             // ok
value = true;           // ok
value.toUpperCase();    // ok, but crashes at runtime if value is not a string

Every line above compiles with no errors or warnings. The compiler trusts you completely, even when a line will clearly fail at runtime because the value is a number instead of a string.

any also spreads through expressions. When you use an any value in a calculation, the result becomes any too, so a single unchecked value can silently disable type checking across an entire function:

typescripttypescript
let input: any = "42";
let doubled = input * 2; // doubled is any, no type checking here either

When Any Is Acceptable

Despite the risks, any has legitimate uses:

  • Gradual migration: when converting a JavaScript file to TypeScript, any lets you add types incrementally.
  • Third-party code without types: when a library has no type declarations and you cannot write them yet.
  • Escape hatches: in rare cases where the type system cannot express a valid pattern, any lets you move forward.

Use any as a temporary tool, not a permanent solution. For more on migration, see migrate JavaScript to TypeScript step by step.

What Unknown Does

unknown is the safe version of any. It accepts every value but prevents you from using that value until you narrow its type. Think of unknown as a sealed box: you know something is inside, but you must open it and check the contents before you can work with it.

typescripttypescript
let data: unknown;
 
data = "hello";
data = 42;
// Both assignments compile because unknown accepts anything.

Reading that value back out is where unknown stops you. Every property access and method call fails until the type is proven:

typescripttypescript
let data: unknown = "hello";
 
data.toUpperCase(); // Error
data.length;        // Error

unknown tells the compiler: "I do not know what this is. Do not let anyone use it without proving its type first."

Narrowing Unknown to a Safe Type

To use an unknown value, you must narrow it. Narrowing means checking the value's runtime type and only using it when the check passes. The flow below shows the only safe path through a typeof check.

Narrowing unknown to a specific type

Every branch in the diagram starts from the same unnarrowed value and only reaches a usable type after a runtime check confirms it. TypeScript tracks the check and narrows the type inside each matching branch of your code, the same way the diagram narrows toward one outcome.

The most common narrowing tool is typeof:

typescripttypescript
function processValue(input: unknown): string {
  if (typeof input === "string") {
    return input.toUpperCase(); // input is string here
  }
 
  if (typeof input === "number") {
    return input.toFixed(2); // input is number here
  }
 
  return "Unsupported type";
}

Inside each if block, TypeScript knows the exact type of input because the typeof check already proved it. The compiler allows toUpperCase in the string branch and toFixed in the number branch, but not the other way around, and calling the function confirms the narrowing works as expected:

typescripttypescript
console.log(processValue("hello")); // "HELLO"
console.log(processValue(42));      // "42.00"

For objects, combine typeof with an in check or a custom type guard before accessing a property. For a deeper look at these techniques, see custom type guards in TypeScript.

The Key Difference in Practice

The difference between any and unknown is clearest when a function receives external data with no known shape.

typescripttypescript
function getLengthAny(value: any): number {
  return value.length; // compiles, but crashes if value has no length
}

The any version compiles without a single warning and crashes at runtime the moment you call it with a number, because nothing ever checked that value actually has a length property. The unknown version below forces that check before the property access is even allowed:

typescripttypescript
function getLengthUnknown(value: unknown): number {
  if (typeof value === "string" || Array.isArray(value)) {
    return value.length; // safe: we proved it has length
  }
  return 0;
}

Both functions do the same job, but only the unknown version can never crash from a type mismatch, because every path either proves the value has a length or falls back to a safe default.

When to Use Each

The table below summarizes when to reach for each type:

ScenarioUse
API response data or JSON.parse() resultunknown
User input from a form or external sourceunknown
Converting a .js file to .ts during migrationany (temporary)
Unavoidable type-system limitationany (rare escape hatch)

The rule is simple: every value that comes from outside your program should start as unknown. Parse it, validate it, narrow it, and only then treat it as a known type. For more on validation patterns, see runtime validation in TypeScript.

Common Mistakes

Using Any Because Narrowing Unknown Seems Like Extra Work

typescripttypescript
function handle(data: any) {
  console.log(data.name.toUpperCase()); // crashes if data has no name
}

The typeof check for unknown takes two lines and prevents a production crash. The extra work is minimal and the safety is permanent.

Narrowing Unknown With an Assertion Without Checking

typescripttypescript
function handle(data: unknown) {
  const name = (data as { name: string }).name; // compiles, but may crash
}

Type assertions override the compiler but do not add any runtime safety. Only use an assertion when you have already checked the type through other means, and prefer typeof and type guards over a bare assertion whenever possible.

Forgetting That Unknown Is Not a Runtime Type

typescripttypescript
let data: unknown = "hello";
 
console.log(typeof data); // "string" (not "unknown")

At runtime, unknown does not exist. The value is whatever JavaScript value it holds, and the unknown type only exists during type checking, erased completely once compilation finishes.

Rune AI

Rune AI

Key Insights

  • any disables type checking; unknown forces you to narrow before use.
  • any is assignable to anything; unknown is only assignable to unknown and any.
  • Narrow unknown with typeof, instanceof, custom type guards, or assertions.
  • Use unknown for API responses, parsed JSON, and external input.
  • Use any only for gradual migration or rare escape hatches.
RunePowered by Rune AI

Frequently Asked Questions

When should I use any instead of unknown?

Use any only for gradual migration from JavaScript, when working with third-party code that has no types, or when you need to opt out of type checking for a specific expression. For all new code, prefer unknown because it preserves safety.

Can I assign unknown to a variable of another type?

No, not without narrowing first. unknown is only assignable to unknown and any. You must check the type with `typeof`, `instanceof`, a type guard, or an assertion before assigning it to a specific type.

Does unknown exist at runtime?

No. Like all TypeScript types, unknown is erased during compilation. At runtime, an unknown variable is just a JavaScript value with no special behavior.

Conclusion

Any and unknown are both universal types that accept any value, but they have opposite safety guarantees. Any tells TypeScript to trust you completely and disables all checks. Unknown tells TypeScript not to trust the value at all until you prove what it is. Use unknown for API responses, user input, parsed JSON, and any value that comes from outside your type system. Reserve any for migration paths and rare escape hatches where you genuinely need to bypass the type checker.