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:
- call methods on it
- access properties on it
- use it like a function
- assign it to more specific types without checking
let value: unknown = "hello";
// value.toUpperCase(); // Error
You must narrow the type first:
let value: unknown = "hello";
if (typeof value === "string") {
console.log(value.toUpperCase());
}
Why this matters
In real programs, you often receive data from places you do not fully control:
- API responses
- user input
- JSON files
- environment variables
- third-party libraries
Using any tells TypeScript to trust the data immediately. Using unknown tells TypeScript to make you validate it first.
That is why unknown is usually the safer default when the actual type is not yet known.
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") {
console.log(valueUnknown.toUpperCase()); // Allowed
}
Example: assigning to other types
let valueAny: any = 42;
let valueUnknown: unknown = 42;
let num1: number = valueAny; // Allowed
// let num2: number = valueUnknown; // Error
unknown cannot be assigned to a more specific type until you check or assert it.
Using a type assertion
let valueUnknown: unknown = "hello";
let text = valueUnknown as string;
console.log(text.toUpperCase());
Type assertions work, but they should be used carefully. A runtime check is usually safer than just asserting.
Example: function input
function printLength(value: unknown) {
if (typeof value === "string" || Array.isArray(value)) {
console.log(value.length);
} else {
console.log("No length available");
}
}
This is a common and safe pattern for working with unknown data.
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.
What happens at runtime
For shout("hello"):
inputis"hello"- the
typeofcheck passes toUpperCase()runs- result:
"HELLO"
For shout(123):
inputis123- the
typeofcheck fails - fallback is returned
- result:
"NOT A STRING"
This is exactly why unknown is useful: it forces you to handle uncertainty safely.
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.
try {
throw "Something went wrong";
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message);
} else {
console.log("Unknown error", error);
}
}
Library boundaries
When building reusable utilities or SDKs, unknown is useful at the edges of your system where input is not trusted yet.
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
function getErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
return String(error);
}
4. Safer wrapper functions
A function may accept unknown, then return a strict type only after checks pass.
function parseCount(value: unknown): number {
if (typeof value === "number") return value;
if (typeof value === "string") return Number(value);
return 0;
}
When any still appears
any is sometimes used:
- during gradual migration from JavaScript to TypeScript
- when a third-party library has poor typings
- in quick prototypes
- in highly dynamic code that is hard to model
Even then, many teams try to isolate any to small areas rather than spreading it through the codebase.
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());
}
}
2. Forgetting to narrow unknown
Broken example:
let value: unknown = "hello";
// console.log(value.length); // Error
Fix:
if (typeof value === "string") {
console.log(value.length);
}
3. Replacing checks with unsafe assertions
let value: unknown = 123;
console.log((value as string).toUpperCase());
This compiles, but may fail at runtime because the assertion does not change the real value.
4. Thinking unknown is useless because it blocks access
That restriction is the main benefit. It pushes validation into your code and catches unsafe assumptions early.
5. Overusing any in function parameters
function handle(data: any) {
return data.items.map((x: any) => x.id);
}
This spreads unsafe typing through the whole function. Prefer validating once, then using proper types afterward.
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
unknowncan be literally any value: string, number, object, null, etc.objectmeans a non-primitive object value.
let a: unknown = 5;
// Allowed
// let b: object = 5; // Error
unknown vs never
unknownmeans "could be anything"nevermeans "this cannot happen"
They are almost opposites in meaning.
Rule of thumb
- Use
unknownwhen you do not know the type yet. - Use
anyonly when you intentionally want to skip type checking.
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
typeofinstanceofArray.isArray()- custom type guards
- equality checks
Assignment rules
let a: any = 123;
let b: unknown = 123;
let n1: number = a; // OK
// let n2: number = b; // Error
Best practice
- Prefer
unknownfor external or untrusted data - Use
anyonly when you truly need an escape hatch
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.
Is unknown the same as object?
No. unknown can be any value at all, while object only represents non-primitive object values.
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
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.