What are any and unknown?
In the diverse world of TypeScript there are 2 special types: any and unknown. This post explains the distinct characteristics and difference between these two types and the hidden danger when using these data types. You will learn when you should and should not use them.
Both any and unknown exist to deliver flexibility in TypeScript's type system and offer a way to bypass TypeScript's type checking. At a glance, any and unknown seem to be similar, however, their behaviors differ significantly:
- any: type tells the compiler to trust you about the type of a variable, and it won't try to check its type during compilation. A variable of type any is like a JavaScript variable, freely mutable and usable in any context.
- unknown: Introduced in TypeScript 3.0, unknown is a more type-safe counterpart of any. While it represents any value (just like any), TypeScript will ensure that you check the type of the variable before using it.
Why not just use any everywhere?
Using any might seem tempting as it offers a coding freedom like in the JavaScript. But this flexibility comes at the cost of type safety. TypeScript's primary advantage is its type system. By using any you're negating the benefits TypeScript offers. For example:
typescriptlet data: any = "Hello, world!"; data = 42; // This code compiles without errors. // Runtime error! Numbers don't have a toLowerCase method. console.log(data.toLowerCase());
The main advantage of TypeScript is providing compilation errors when the code has some mistakes with type usage. This prevents runtime errors that JavaScript applications mostly struggle of. Using type any for data variable in this example tells compiler to skip type checking for the variable usage. As a result an error is thrown in the runtime because data variable is not of type string and doesn't contain toLowerCase method.
What about unknown?
When using unknown, TypeScript ensures you perform type checks, making your code safer.
typescriptlet data: unknown = "Hello, world!"; data = 42; // This code compiles without errors. // This won't compile: // console.log(data.toLowerCase()); if (typeof data === "string") { console.log(data.toLowerCase()); // Now it compiles! }
This code is much safer than the previous example: it only allows to call the toLowerCase method if the type of data variable is string. In this case data variable has type number, that way code inside the if statement will not execute during the runtime.
Practical Use Cases for any and unknown:
- API Responses: When you're unsure about the shape of data coming from an API, or shape can take different forms, unknown is a safer choice than any. It forces you to perform necessary checks before using the data.
- Reusable Utilities: For utility functions that accept diverse input, unknown ensures that the type-specific logic inside the function is guarded against unexpected input types.
- Integration with javascript: you can use any if you can't create concrete types when integrating with existing javascript code.
Summary
TypeScript's type system is a balance of flexibility and safety. While any gives you a quick and easy way out of the type system, unknown tries to retain TypeScript's type safety. Understanding the distinction between any and unknown is important to create resilient and maintainable TypeScript applications.
In TypeScript, the any and unknown types are tools that provide flexibility, especially when dealing with dynamic data or integrating with JavaScript. However, this flexibility often comes at the cost of type safety, which is TypeScript's primary strength. By utilizing these types, you run the risk of introducing runtime errors and decreasing the code readability and maintainability.
While there are certain scenarios, you should avoid any and unknown at all costs, us them only as the last available option.
Instead use concrete types and reveal the full power of TypeScript's static type system to your advantage.
Hope you find this blog post useful. Happy coding!