全部 / 前端 / 技术 · 2022年9月10日 0

TypeScript – unknown vs any

原文地址:https://dmitripavlutin.com/typescript-unknown-vs-any/

any 类型的变量可以被赋值任何值:

let myVar:any = 0;
myVar = '1';
myVar = false;

许多 TypeScript 指南不建议使用 any ,因为它会丢掉类型限制 — 这恰是为何使用 TypeScript 的原因!

TypeScript (3.0+ 以上版本)还提供了一个特殊的类型 unknown,类似于 any。你同样也可以向 unkonwn 类型的变量赋值任何值:

TypeScript 3.0 introduces a new top type unknown. unknown is the type-safe counterpart of any. Anything is assignable to unknown, but unknown isn’t assignable to anything but itself and any without a type assertion or a control flow based narrowing. Likewise, no operations are permitted on an unknown without first asserting or narrowing to a more specific type.

let myVar:unknown = 0;
myVar = '1';
myVar = false;

现在的最大问题是:anyunknown 的区别是什么?

让我们在这篇文章中找到:

1. unknown vs any

为了更好的理解 unknownany 的区别,让我们写一个函数以及内部调用它的唯一参数。

我们使 invokeAnything() 的参数为 any 类型:

function invokeAnything(callback: any) {
  callback();
}
 
invokeAnything(1);

因为 callbackany 类型,callback() 语句不会触发类型错误,你可以对 any 类型的变量做任何事。

但是运行时脚本会抛出一个运行时错误:TypeError: callback is not a function1 是一个数字不能被当做函数调用 — 同时 TypeScript 没有保护你免受此种错误的影响!

如何允许 invokeAnythings() 函数接受任意类型的参数,但是强制对参数进行类型校验,例如:如果以函数来调用它?

欢迎 unknown!

unknown 类型的变量与 any 类型的类似,接受任意值。但是当尝试使用 unknown 的变量时,TypeScript 强制一个类型校验。以此确保正是你所需的。

我们来把 callback 参数的类型从 any 改为 unknown,然后看看发生了什么:

function invokeAnything(callback: unknown) {
  callback();
Object is of type 'unknown'.
}
 
invokeAnything(1);

因为 callback 参数是 unknown 类型,callback() 语句有一个类型错误 Object is of type 'unknown'。现在,与 any 相反,当调用时 TypeScript 使你免受参数非函数类型的错误。

在使用 unknown 类型的变量之前,你需要进行类型校验。在这个例子中,你需要简单的校验 callback 是否为函数类型:

function invokeAnything(callback: unknown) {
  if (typeof callback === 'function') {
    callback();
       
(parameter) callback: Function
  }
}
 
invokeAnything(1);

已经添加了 typeof callback === 'function' 校验,你可以安全的调用 callback() 因为 unknown 已经缩小为 Function 类型。没有类型错误和运行时错误!非常棒!

2. unknown 与 any 的心智模型

说实在的,当我开始学习 unknown 时,理解起来确实困难。由于它与 any 都可以接收任何值,那到底有什么区别呢?

下面是帮助我理解它们不同的准则:

  1. 你可以给 unknown 类型的变量赋值任何值,但是对它进行操作之前必须类型检查或类型断言。
  2. 你可以把 unknown 想象为 type unknown : number | string | boolean | ...
  3. 你可以给 any 类型的变量赋值任何值,以及给对它进行任何操作。

上面的代码已经很明白的演示了 unknownany 之间的异同。

unknown 的例子:

function invokeAnything(callback: unknown) {
  if (typeof callback === 'function') {
    callback();
  }
}
 
invokeAnything(1);

这里的类型检查是 typeof callback === 'function' — 检测 callback 是否为一个函数。callback 的类型被限制为函数类型。

any 的例子:

function invokeAnything(callback: any) {
  callback();
}
 
invokeAnything(1);

callbackany 类型,TypeScript 不会对 callback() 进行任何类型检查。

3. 总结

unknownany 是两个特殊的可以接受任何值的类型。

因为 unknown 提供了类型安全,所以相比于 any 更推荐它 — 若你想对 unknown 进行操作前必须类型校验或缩小到特定的类型。