TypeScript odd types: unknown, void, never, any, null and undefined
TypeScript has a very complex and powerful type systems. Yet for some it also constitutes a mystery in its more deep features. Among these, special types can be source of brain freezes for many, so let’s see in detail what they all mean.
Void
The void type represents the absence of any type. It is notably used to describe the return value of functions that do not return anything. No value can be assigned to a variable of type void, except for undefined
. Additionally, if you do not have the --strictNullChecks
option enabled on your TypeScript compiler, you can in principle also assign null
to void variables. However, I highly advised you avoid that and always have --strictNullChecks
enabled, as it enforces a more secure and predictable application behaviour. Besides, it allows you to worry less on whether or not you should check for nullity.
Any
At the opposing end of the spectrum is the any type, which represents literally any type. A variable whose type is any
can be assigned any possible value and it will never produce a compilation error. Similarly, a function that returns any
can return arbitrary objects, atomic values or even nothing, and will still be valid. Additionally, you can always access arbitrary properties and methods from a variable of any
type without the compiler complaining. On the downside, you have no guarantee that the actual value contained there at runtime will abide to your expectations. Working with any
is occasionally unavoidable, especially when dealing with libraries written in pure javascript or with native bindings. At the same time, I discourage sweeping warnings under the rug. If possible, get type definition for third party libraries from typing repositories like DefinitelyTyped or write your type declarations yourself.
Unknown
The unknown type is similar to any
, in that it also represents a type that is apriori not known. However, it behaves slightly differently than any
:
- any value can be assigned to a variable of type
unknown
or returned from a function declared withunknown
return type; - however, while
any
can be assigned without warnings or errors to any other type,unknown
cannot and will cause a compilation error; - additionally, if you try to access any property or method on a variable of
unknown
type, the compiler will point it out as an error.
unknown
is safer to usa than any
, since it restricts all operations you can perform on variables and return values. I also like to think of unknown
as “I don’t care what type it is” and find it especially useful for defining callbacks. Using it for declaring callbacks is one of my favourite usages:
Never
The never type is very interesting. It represents the type of values that can never occur. While in principle it may make little sense, it does have many interesting application:
- functions that always throw an exception implicitly have
never
as their return type; - functions that trivially cannot terminate also have a
never
return type, but of course there is a limit to the compiler’s ability to detect this scenario; - when a union or more complex type is progressively narrowed down through subsequent type guards and eventually reach a point where all options have been exhausted and there is no other possible type left. The type of the analyzed expression is
never
in this case.
Null and undefined
Null and undefined are singleton types. null
is the type of the singleton value null
, and undefined
is type type of single value undefined
. Duh. If you have the --strictNullChecks
compiler option enabled (as you should), they can only be assigned to unknown
or any
directly. Otherwise, you need to specify that your values may accept either of those special symbols by using a union type.