Difference Between 'any,' 'unknown' and 'never' in TypeScript

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.

  1. Any type is assignable to the any type.
  2. 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.

image.png

The rules for unknown

The unknown type obeys only the first rule:

  1. 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.

image.png

The rules for never

The never type obeys only the second rule:

  1. 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.

image.png

Which to choose

Which type to choose depends on what you're doing.

  1. Are you migrating a JavaScript codebase to TypeScript?
  2. Are you expecting an unknown type?
  3. 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

  1. Are you migrating a JavaScript codebase to TypeScript? Keep any handy.
  2. Are you expecting an unknown type? Prefer using the unknown type.
  3. Are you expecting a function to never run? Reach for the never type.

Interesting areas of reference

  1. What is Type Theory
  2. What is a Bottom Type
  3. Why does any exist
  4. Why never exists