Understanding any and unknown in TypeScript. Difference between never and void

JavaScript TypeScript

When implementing TypeScript in our projects, we strive to write the best typings we can. We might often feel like using the   type defeats the purpose of TypeScript, and rightfully so. There are also some other types worth knowing, and we might find them useful when trying not to use  , like the  . In this article, we also discuss  and .

Any

The   type resembles how working with a pure JavaScript would be like. We sometimes might need to describe a variable that we don’t know the type of at all.

In TypeScript, everything is assignable to any. It is often referred to as a top type.

Writing the code in such a manner does not seem proper. It is unpredictable and hard to maintain. You might feel the need to use it when dealing with some third-party libraries that have no typings created for them, and you are not sure how they work. Also, using  might be a way to add TypeScript to your existing JavaScript codebase.

By using  , we undermine the ability of TypeScript to prevent us from causing trouble. There is no type-checking enforced, and therefore it might give you some headaches.

TypeError: uncertain.hello is not a function

And there you go, an error ready to ship to production! The above example is very vivid, but it could be more subtle.

TypeError: uncertain.hello is not a function

It would be beneficial to figure out some more detailed typings.

Unknown

The   type introduced in TypeScript 3.0 is also considered a top type, but a one that is more type-safe. All types are assignable to  , just as with  .

We can assign a variable of the   type only to , and the  type.

It does differ from  in more ways. We can’t perform any operations on the   type without narrowing the type.

Unable to compile TypeScript:
Property ‘hello’ does not exist on type ‘unknown’.

Narrowing down the unknown with type assertions

The above mechanism is very preventive but limits us excessively. To perform some operations on the  type, we first need to narrow it, for example, with a type assertion.

It the code above, we force the TypeScript compiler to trust that we know what we are doing.

A significant disadvantage of the above is that it is only an assumption. It has no run-time effect and does not prevent us from causing errors when done carelessly.

TypeError: number.toLowerCase is not a function

The TypeScript compiler receives an assumption that our number is a string, and therefore it does not oppose treating it as such.

Using control-flow based narrowing

A more type-safe way of narrowing down the  type is to use a control-flow narrowing.

The TypeScript compiler analyses our code and can figure out a narrower type.

In the code above, we check the type of the   variable in the run-time. Therefore, we can be sure that we call the   function only if the   is a variable.

Aside from using , we can also make use of   to narrow the type of a variable.

In the code above, we make sure that we call   only if our variable is an instance of a certain prototype. TypeScript compiler understands that and assumes the type.

If you want to know more about prototypes, check out Prototype. The big bro behind ES6 class

There is currently an interesting suggestion stating that TypeScript should also use the in operator when narrowing types to assert property existence. If you’re interested, check out this issue in the TypeScript repository.

A workaround provided by Tom Crockett is to use a custom   type guard.

Differences between Void and Never

The  and  types are often used as return types of functions. To avoid confusion, let’s compare them.

Void

The  type acts as having no type at all.

It is useful to indicate that we are not supposed to use the return value of the above function. We can spot the difference between the   and   here:

The  type is common in other languages like C++, where it serves a similar purpose.

Never

The   type indicates that a function never returns. We sometimes refer to it as the bottom type.

A typical example is when a function always throws an error:

Another example is when the function has a loop that never ends.

Also, the TypeScript compiler asserts the   type if we create an impossible type guard:

Property ‘toLowerCase’ does not exist on type ‘never’.

Summing up both  and  :

  • A function that does not return any value explicitly has a return value of undefined. To indicate that we ignore it, we use the   return type
  • A function that never returns at all due to some reasons has a return type of 

Summary

In this article, we’ve gone through the differences between   and  . A conclusion from comparing the above is that the  type is a lot safer because it forces us to do additional type-checking to perform operations on the variable. We’ve also gone through the   and   types. By doing so, we differentiate functions that don’t return values from the ones that don’t return at all.

avatar
  Subscribe  
Notify of