Question
In TypeScript, unknown was introduced as a built-in type in TypeScript 3.0. What is the difference between unknown and any? When should unknown be used instead of any?
For example, how do these types behave differently?
let valueAny: any = "hello";
let valueUnknown: unknown = "hello";
valueAny.toUpperCase();
valueUnknown.toUpperCase();
Short Answer
By the end of this page, you will understand how any and unknown differ in TypeScript, why unknown is safer, and when each type should be used. You will also see how unknown forces type checks before use, which helps prevent runtime errors in real applications.
Concept
any and unknown both represent values whose type is not known ahead of time, but they behave very differently.
anyturns off type checking for that value.unknownkeeps the fact that the type is unknown, so TypeScript requires you to prove what it is before using it.
What any means
When a value is typed as any, TypeScript allows almost anything:
let value: any = "hello";
value.toUpperCase();
value.doesNotExist();
value();
TypeScript will not complain, even if the code is clearly unsafe. This makes any an escape hatch.
What unknown means
When a value is typed as unknown, TypeScript says:
"I know this value exists, but I do not know what it is yet."
Because of that, you cannot directly:
Mental Model
Think of these two types like packages.
anyis a package with no rules. You are allowed to open it, use it, or assume anything is inside.unknownis a sealed package with a warning label: "Contents not verified." You must inspect it before using it.
With any, TypeScript steps aside and says, "You are on your own."
With unknown, TypeScript says, "I will help you stay safe until you confirm what this value is."
Syntax and Examples
The main difference appears when you try to use the value.
Basic syntax
let a: any;
let b: unknown;
Example: method calls
let valueAny: any = "hello";
let valueUnknown: unknown = "hello";
console.log(valueAny.toUpperCase()); // Allowed
// console.log(valueUnknown.toUpperCase()); // Error
valueUnknown.toUpperCase() fails because TypeScript does not yet know that valueUnknown is a string.
Narrowing unknown
let valueUnknown: unknown = "hello";
if (typeof valueUnknown === "string") {
.(valueUnknown.());
}
Step by Step Execution
Consider this example:
function shout(input: unknown) {
if (typeof input === "string") {
return input.toUpperCase();
}
return "NOT A STRING";
}
console.log(shout("hello"));
console.log(shout(123));
Step by step
- The function parameter
inputis declared asunknown. - That means the function accepts any value, but cannot use it unsafely.
- The
if (typeof input === "string")check narrowsinputfromunknowntostringinside that block. - Inside the block,
input.toUpperCase()is allowed because TypeScript now knowsinputis a string. - If the input is not a string, the function returns a fallback value.
Real World Use Cases
API responses
When data comes from an external API, the shape may not be guaranteed.
async function fetchUser(): Promise<unknown> {
const response = await fetch("/api/user");
return response.json();
}
You can then validate the data before using it.
Parsing JSON
const raw = '{"name":"Ada"}';
const data: unknown = JSON.parse(raw);
JSON.parse() returns data that could be anything. Treating it as unknown is safer than trusting it immediately.
User input
Form values, query parameters, and CLI arguments often need validation before use.
Catching errors
In modern TypeScript, caught errors are often treated cautiously because they may not always be Error objects.
Real Codebase Usage
In real projects, unknown is commonly used at boundaries and any is avoided unless there is a strong reason.
Common patterns
1. Validation at the edges
Developers often accept unknown first, then validate.
function isUser(value: unknown): value is { name: string } {
return (
typeof value === "object" &&
value !== null &&
"name" in value
);
}
2. Guard clauses
function greet(value: unknown) {
if (typeof value !== "string") {
return "Invalid name";
}
return `Hello, ${value}`;
}
This keeps unsafe values out of the main logic.
3. Error handling
Common Mistakes
1. Using any as the default for unknown data
Broken example:
function printName(user: any) {
console.log(user.name.toUpperCase());
}
This compiles, but can crash if user.name is missing or not a string.
Safer version:
function printName(user: unknown) {
if (
typeof user === "object" &&
user !== null &&
"name" in user &&
typeof (user as { name: unknown }).name === "string"
) {
console.log((user as { name: string }).name.toUpperCase());
}
}
Comparisons
| Feature | any | unknown |
|---|---|---|
| Can hold any value | Yes | Yes |
| Can call methods without checks | Yes | No |
| Can access properties without checks | Yes | No |
| Can be assigned to other types freely | Yes | No |
| Requires type narrowing | No | Yes |
| Type safety | Very low | High |
| Best use case | Escape hatch, migration, difficult typing | Untrusted input, external data, safe boundaries |
unknown vs object
Cheat Sheet
let x: any;
let y: unknown;
any
- Disables type checking for that value
- You can call methods, access properties, and assign it anywhere
- Useful as a last resort
let value: any = "hello";
value.toUpperCase();
value.notReal(); // still allowed by TypeScript
unknown
- Accepts any value
- Cannot be used directly until narrowed
- Safer than
any
let value: unknown = "hello";
if (typeof value === "string") {
value.toUpperCase();
}
Narrowing tools
typeof
FAQ
Is unknown safer than any in TypeScript?
Yes. unknown is safer because it forces you to check the value before using it.
Should I always use unknown instead of any?
In most cases, yes when the type is not known yet. Use any only when you intentionally want to bypass type checking.
Why does unknown cause errors when I access properties?
Because TypeScript does not know whether that property exists. You must narrow the type first.
Can unknown hold a string, number, or object?
Yes. unknown can store any value.
How do I convert unknown into a specific type?
Use type narrowing with checks like typeof, instanceof, or a custom type guard. You can also use a type assertion, but that is less safe.
When is any acceptable?
It can be acceptable during migration, when working around bad library typings, or in small isolated areas where strict typing is not practical.
Mini Project
Description
Build a small TypeScript utility that safely reads unknown API-like data and extracts a user's name. This demonstrates the main value of unknown: accepting uncertain input, validating it, and only then using it as a trusted type.
Goal
Create a function that accepts unknown input and returns a safe user name string without risking invalid property access.
Requirements
- Accept one input typed as
unknown - Return the user's name if the input contains a valid string
nameproperty - Return a fallback string such as
"Anonymous"when the input is invalid - Use runtime checks instead of relying only on type assertions
Keep learning
Related questions
Angular formGroup Error Explained: Fixing 'Can't bind to formGroup' in Reactive Forms
Learn why Angular shows 'Can't bind to formGroup' and how to fix it by importing ReactiveFormsModule correctly.
Fix "Element implicitly has an 'any' type" in TypeScript Object Indexing
Learn why TypeScript rejects string object indexing and how to fix it with keyof, unions, and typed object keys in React.
Fix "Property has no initializer" in Angular TypeScript Components
Learn why Angular TypeScript shows "Property has no initializer" and how to fix it using defaults, optional properties, or definite assignment.