- Published on
TypeScript Comparing 'any' and 'unknown' Types
- Authors
- Name
- Yinhuan Yuan
Introduction
TypeScript, a statically typed superset of JavaScript, has gained immense popularity due to its ability to catch errors at compile-time and provide better tooling support. Two of its type annotations, any
and unknown
, are often misunderstood or used interchangeably. In this blog post, we'll dive deep into these types, explore their differences, and discuss when to use each.
The 'any' Type
The any
type is TypeScript's escape hatch from its type system. It allows you to opt-out of type checking for a particular value.
Characteristics of 'any':
- No Type Checking: Variables of type
any
can hold values of any type. - Allows All Operations: You can perform any operation on an
any
type without type checks. - Propagates through Operations: Using an
any
value in an operation results in anany
type.
Example of 'any':
let anyValue: any = 10
anyValue = 'Hello' // OK
anyValue = true // OK
let num: number = anyValue // OK (but potentially unsafe)
console.log(anyValue.nonExistentMethod()) // No compile-time error
The 'unknown' Type
Introduced in TypeScript 3.0, the unknown
type is a type-safe counterpart of any
.
Characteristics of 'unknown':
- Type-safe: Variables of type
unknown
can hold any value, but TypeScript won't allow operations on anunknown
without type checks. - Requires Type Checking: You must narrow the type (through type guards or assertions) before performing operations.
- Doesn't Propagate: Using an
unknown
value in an operation doesn't implicitly spread theunknown
type.
Example of 'unknown':
let unknownValue: unknown = 10
unknownValue = 'Hello' // OK
unknownValue = true // OK
let num: number = unknownValue // Error: Type 'unknown' is not assignable to type 'number'
if (typeof unknownValue === 'number') {
let num: number = unknownValue // OK
}
console.log(unknownValue.toString()) // Error: Object is of type 'unknown'
Comparing 'any' and 'unknown'
Type Safety:
any
: Bypasses all type checks.unknown
: Enforces type checks, making it much safer.
Assignability:
any
: Can be assigned to any other type.unknown
: Can only be assigned tounknown
orany
.
Operations:
any
: Allows all operations without checks.unknown
: Requires type narrowing before operations.
Best Practices:
- Use
any
when you need to opt-out of type checking entirely (e.g., when working with dynamic content or during migration from JavaScript). - Use
unknown
when you want to accept any value but ensure type-safe operations later.
- Use
When to Use 'unknown'
- API Responses: When working with responses from APIs where the structure isn't known at compile-time.
async function fetchData(): Promise<unknown> {
const response = await fetch('https://api.example.com/data')
return response.json()
}
const data = await fetchData()
if (typeof data === 'object' && data !== null && 'name' in data) {
console.log(data.name) // TypeScript knows 'data' has a 'name' property
}
- Type Assertions: When you need to work with a value whose type you know but TypeScript doesn't.
function processValue(value: unknown) {
if (typeof value === 'string') {
console.log(value.toUpperCase())
} else if (Array.isArray(value)) {
console.log(value.length)
}
}
Conclusion
While any
provides flexibility, it comes at the cost of type safety. The unknown
type offers a balance between flexibility and safety, encouraging developers to think about types and perform necessary checks.
As a best practice:
- Avoid
any
unless absolutely necessary. - Use
unknown
when you need to accept any value but want to maintain type safety. - Always narrow
unknown
types before performing operations on them.
By understanding and correctly using any
and unknown
, you can write more robust and maintainable TypeScript code, leveraging the full power of TypeScript's type system while still handling dynamic or unknown data when needed.