A variable of type any can be assigned with anything:
let myVar: any = 0;myVar = '1';myVar = false;
Many TypeScript guides discourage the use of any because using it throws away the type restrictions — the first reason why you use TypeScript!
TypeScript (version 3.0 and above) also provides a special type unknown that is similar to any. You can assign any value to an unknown type variable as well:
let myVar: unknown = 0;myVar = '1';myVar = false;
Now... the big question is: what is the difference between using any and unknown?
Let's find out in this post.
1. unknown vs any
To better understand the difference between unknown and any, let's start with writing a function that wants to invoke its only argument.
Let's make the only parameter of invokeAnything() as any type:
function invokeAnything(callback: any) { callback();}invokeAnything(1);
Because callback param is of any type, the statement callback() won't trigger type errors. You can do anything with a variable of type any.
But running the script throws a runtime error: TypeError: callback is not a function. 1 is a number and cannot be invoked as a function — and TypeScript hasn't protected you from this error!
How to allow invokeAnything() function to accept any kind of argument, but force a type check on that argument, for example, if invoking it as a function?
Welcome unknown!
An unknown type variable, same as any, accepts any value. But when trying to use the unknown variable, TypeScript enforces a type check. Exactly what you need!
Let's change the type of callback param from any to unknown, and see what happens:
function invokeAnything(callback: unknown) { callback(); // Type error: 'callback' is of type 'unknown'}invokeAnything(1);
Because the callback argument is of type unknown, the statement callback() has a type error Object is of type 'unknown'. Now, contrary to any, TypeScript protects you from invoking something that might not be a function!
You need to perform type checking before using a variable of type unknown. In the example, you would simply need to check if callback is a function type:
function invokeAnything(callback: unknown) { if (typeof callback === 'function') { callback(); }}invokeAnything(1);
Having added typeof callback === 'function' check, you can safely invoke callback() because unknown has narrowed to Function type. No type errors and no runtime errors! Great!
2. The mental model of unknown vs any
To be honest, I had difficulties understanding unknown when I had been learning it. How does it differ from any, since both types accept any value?
Here's the rule that had helped me understand the difference:
- You can assign anything to
unknowntype but you have to do a type check or type assertion to operate onunknown - You can assign anything to
anytype and you can perform any operation onany
The example above has demonstrated exactly the similarity and difference between unknown and any.
The case of unknown:
function invokeAnything(callback: unknown) { if (typeof callback === 'function') { callback(); }}invokeAnything(1);
The type check here is typeof callback === 'function' — checking whether the callback is a function. The type of callback narrows to function type.
The case of any:
function invokeAnything(callback: any) { callback();}invokeAnything(1);
callback being any, TypeScript doesn't enforce any type checking for the statement callback().
3. Conclusion
unknown and any are 2 special types that can hold any value.
unknown is recommended over any because it provides safer typing — you have to use type assertion or narrow to a specific type if you want to perform operations on unknown.
Challenge: can you write a utility type IsUnknown<T> which evaluates to true if T is unknown and false otherwise?


