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

栏目: IT技术 · 发布时间: 4年前

内容简介:When implementing TypeScript in our projects, we strive to write the best typings we can. We might often feel like using theTheIn TypeScript, everything is assignable to any. It is often referred to as a

When implementing TypeScript in our projects, we strive to write the best typings we can. We might often feel like using the any 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  any , like the  unknown . In this article, we also discuss  never and void .

Any

The any 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.

let uncertain: any = 'Hello world'!;
uncertain = 5;
uncertain = { hello: () => 'Hello world!' };

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 any might be a way to add TypeScript to your existing JavaScript codebase.

By using any , 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.

const uncertain: any = 'Hello world!';
uncertain.hello();
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.

const dog: any = {
  name: 'Fluffy',
  sayHello: () => 'woof woof'
};
 
dog.hello();
TypeError: uncertain.hello is not a function

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

Unknown

The unknown 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  unknown , just as with  any .

let uncertain: unknown = 'Hello'!;
uncertain = 12;
uncertain = { hello: () => 'Hello!' };

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

let uncertain: unknown = 'Hello'!;
let notSure: any = uncertain;

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

const dog: unknown = getDog();
dog.hello();
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 unknown type, we first need to narrow it, for example, with a type assertion.

type getDogName = () => unknown;
const dogName = getDogName();
console.log((dogName as string).toLowerCase());

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.

const number: unknown = 15;
(number as string).toLowerCase();
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 unknown type is to use a control-flow narrowing.

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

const dogName = getDogName();
 
if (typeof dogName === 'string') {
  console.log(dogName.toLowerCase());
}

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

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

type getAnimal = () => unknown;
const dog = getAnimal();
 
if (dog instanceof Dog) {
  console.log(dog.name.toLowerCase());
}

In the code above, we make sure that we call dog . name . toLowerCase 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  hasKey type guard.

const dog = getAnimal();
 
if (typeof dog === 'object' && hasKey('name', dog)) {
  console.log(dog.name);
}

Differences between Void and Never

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

Void

The void type acts as having no type at all.

function sayHello(): void {
  console.log('Hello world!');
}

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 void and  undefined here:

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

Never

The never 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:

function throwUserNotFoundError(userId: number): never {
  throw new Error(`User with id ${userId} is not found`);
}

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

function renderGame(game: Game): never {
  while (true) {
    game.renderFrame();
  }
}

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

const uncertain: unknown = 'Hello world!';
 
if (typeof uncertain === 'number' && typeof uncertain === 'string') {
  console.log(uncertain.toLowerCase());
}
Property ‘toLowerCase’ does not exist on type ‘never’.

Summing up both never and  void :

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

Summary

In this article, we’ve gone through the differences between any and  unknown . A conclusion from comparing the above is that the  unknown 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  never and  void types. By doing so, we differentiate functions that don’t return values from the ones that don’t return at all.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Little Schemer - 4th Edition

The Little Schemer - 4th Edition

Daniel P. Friedman、Matthias Felleisen / The MIT Press / 1995-12-21 / USD 40.00

This delightful book leads you through the basic elements of programming in Scheme (a Lisp dialect) via a series of dialogues with well-chosen questions and exercises. Besides teaching Scheme, The Lit......一起来看看 《The Little Schemer - 4th Edition》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具