Difference Between 'any,' 'unknown' and 'never' in TypeScript
Notes from Effective TypeScript by Dan Vanderkam
Motivation
Try as we might, we won't always know what a type should be at every given point in time. In these situations it's important that we choose a suitable way to represent such a type which is both safe and broad enough. This is where any
, unknown
and never
come in.
Primer
Recall that with Typescript we have these assignability rules for the any
type.
- Any type is assignable to the
any
type. - The
any
type is assignable to any other type.
Keep these in mind as we move on to talk about any
, unknown
, and never
in terms of assignability rules.
The rules for any
The any
type obeys the two rules from our primer, but it's worth noting that the second rule isn't completely true, because the any
type is not assignable to a type of never
.
The rules for unknown
The unknown
type obeys only the first rule:
- Any type is assignable to the
unknown
type
but not the second rule, because we can only assign the unknown
type to another unknown
type or the type any
.
The rules for never
The never
type obeys only the second rule:
- The
never
type is assignable to any other type
but not the first rule, because we can never assign any other type to the type never
, not even the type any
.
Which to choose
Which type to choose depends on what you're doing.
- Are you migrating a JavaScript codebase to TypeScript?
- Are you expecting an unknown type?
- Are you expecting a function to never run?
1. Are you migrating a JavaScript codebase to TypeScript?
Then keep the any
type handy to tell the type checker to ignore some types while you perform your migration -- this seems to be the main reason any
was created in the first place[3].
In essence, use the any
type when you are sure you want to ignore type checking on a variable.
Other than this, I personally don't see a strong use case for any
in a TypeScript codebase, given how dangerous it can be in allowing errors seep through from one function to another.
2. Are you expecting an unknown type?
In cases where a type is unknown, it's advisable to use the unknown
type and avoid the use of any
. The main reason for this is because 'any' effectively silences the type checker when used, causing errors such as undefined to sneak in.
Instead, prefer unknown
. The beauty of using 'unknown' to represent an unknown type is that when we try to access the properties of an 'unknown' type, the type checker reminds us that we're trying to perform a dangerous operation.
Now, the very fact that we're trying to access a property of an unknown type means we now know something about that previously unknown type. So a warning from TypeScript at that point is a great reminder to update our unknown
type with the details we now know about it, before trying to access it.
3. Are you expecting a function to never run?
As for never, it doesn't really help when dealing with migrations or values of unknown types, but it is the perfect type to use when we know a function will never run or never return anything.
This is different from void
, in that we can expect a void function to return even after performing operations with no side effects or return values.
However, a never function will never return... it might throw an error, run infinitely, or simply represent code that will never be reached.
Conclusion
- Are you migrating a JavaScript codebase to TypeScript? Keep
any
handy. - Are you expecting an unknown type? Prefer using the
unknown
type. - Are you expecting a function to never run? Reach for the
never
type.