TypeScript's `unknown` type
Any why you should stop declaring all variables as type `any`
Table of contents
In TypeScript, both unknown
and any
types represent values that could be anything. However, they are used in quite different contexts due to their safety characteristics.
any
Type
The any
type is essentially TypeScript's way of saying, "turn off type checking for this variable." When you use any
, you're telling TypeScript to let you work with a variable without enforcing any type checks. This can be useful when you're dealing with dynamic content that you don't have type information for, but it comes at the cost of losing type safety, making the code prone to runtime errors.
Using any
disables TypeScript's type checking, which is a key feature of the language to catch errors at compile time.
anything: any = "hello";
anything = 5; // No error
anything = {}; // No error
unknown
Type
The unknown
type represents any value, similar to any
. However, unknown
is much safer because TypeScript enforces that you perform some kind of type checking before you can use the value.
When you use unknown
, TypeScript ensures that you can't just assume the type of the value and must explicitly check or assert the type before performing operations on it. This adds a layer of type safety, making sure that you're aware of the variable's type and its capabilities before using it.
let something: unknown = "hello";
// TypeScript will enforce a type check or assertion
if (typeof something === "string") {
console.log(something.toUpperCase()); // This is safe
}
// Directly calling methods or properties will result in an error
// something.toUpperCase(); // Error: Object is of type 'unknown'.
When to Use unknown
unknown
is preferred over any
when you want to maintain type safety for variables that come from dynamic or unknown sources. It forces you to perform explicit type checking or casting, reducing the risk of runtime errors. It's particularly useful in the following scenarios:
- API responses: When you're not sure of the structure of the response.
async function fetchUserData(url: string): Promise<unknown> {
const response = await fetch(url);
return response.json();
}
// You must check the type before using it
const userData = await fetchUserData("https://api.example.com/user");
if (typeof userData === "object" && userData !== null && "username" in userData) {
console.log(userData.username); // Safe to access `username`
}
- Third-party libraries: When interacting with libraries without type definitions, or if the library can return multiple types.
declare function getLibraryData(): unknown;
const data = getLibraryData();
if (Array.isArray(data)) {
// Now it's safe to treat `data` as an array
data.forEach(item => console.log(item));
}
- Error handling: When catching errors, as TypeScript types error objects as
unknown
.
try {
// Some operation that may fail
} catch (error: unknown) {
if (error instanceof Error) {
console.log(error.message);
} else {
console.log("An unknown error occurred");
}
}
While any
provides the ultimate flexibility by opting out of type checking, unknown
offers a balance between flexibility and safety, ensuring that any operations on unknown types are explicitly checked.