一文搞懂 TypeScript 中的 ‘any‘ 和 ‘unknown‘ 类型
掌握any类型和unknown类型之间的相似性和差异,避免把 TypeScript 当成 AnyScript 来学习。
当你第一次开始使用 TypeScript 时,你是否遇到过很多疯狂的问题,最终你用 any
技巧解决了它们?如果你以后不花时间学习 TypeScript 的类型系统,你可能会发现你把 TypeScript 当成了 AnyScript。
在 TypeScript 中, any
类型被称为顶级类型,所谓顶级类型可以理解为泛型父类型,即可以包含所有值的类型。
在 TypeScript 3.0 中,引入了新的顶级类型 — unknown
类型,同样,你可以给未知类型的变量赋值。
那么现在问题来了, any
类型和 unknown
类型的区别是什么?any 类型可以理解为我不关心它的类型, unknown
类型可以理解为我不知道它的类型。
事实上,any 类型本质上是类型系统的逃生口,TypeScript允许我们对 any 类型的值执行任何操作,而无需执行任何类型的预先检查。
这有什么问题呢?让我们举个例子:
对于上面的 TypeScript 代码,编译时不会提示任何错误,但是在运行时会抛出运行时错误。作为开发者, any
类型给了我们很多自由,但它也带来了一些陷阱。为了解决 any
类型的安全风险,TypeScript 团队在 3.0 中引入了 unknown
类型,你可以理解为类型安全的 any
类型。
那么, unknown
类型在什么地方体现了类型安全呢?在这里,我们把 callWithErrorHandling
函数参数的类型改为 unknown
类型,然后 TypeScript 编译器会提示相应的错误消息:
相对于 any
类型,TypeScript对 unknown
类型的参数进行类型检查,以避免fn参数不是函数类型,为了解决上述问题,我们需要缩小fn参数的类型范围,即使用 typeof
操作符来确保fn参数是函数类型的对象:
在实践中,你还可以通过 instanceof 或用户定义的类型保护来缩小参数的类型范围:
declare function isFunction(x: unknown): x is Function;
function fn(x: unknown) {
if (x instanceof Error) {
x; // Error
}
if (isFunction(x)) {
x; // Function
}
}
与 any
类型不同,因为TypeScript对 unknown
类型的变量进行类型检查,当我们将上述代码中值变量的类型更改为 unknown
类型时,所有使用该值变量的语句都将失败。
此外,应该注意,unknown
类型的变量只能赋给 any 类型和 unknown
类型本身。
我们在之前的文章中已经介绍过 keyof 操作符和 mapped 类型。 any
类型和 unknown
类型在以下情况下表现不同:
type A0 = keyof any; // string | number | symbol
type U1 = keyof unknown; // never
type M2<T> = { [P in keyof T]: number };
type M21 = M2<any>; // { [x: string]: number }
type M22 = M2<unknown>; // {}
在上面的代码中,M2 类型被称为映射类型,在映射过程中,如果键类型为 never
类型,则当前键将被过滤掉,因此 M22 类型是一个空对象类型。
在工作中,为了保证类型安全,我们应该尽可能使用 unknown
类型,最后,让我们来看看 unknown
类型在不同类型下进行类型运算的结果:
any
类型很特殊。当构造或与任何类型组合时,返回的是 any
类型。在阅读完本文后,我相信您已经理解了 any
类型和 unknown
类型之间的区别。您知道如何检测 any
类型和 unknown
类型吗?如果您知道,可以在评论部分提交您的答案。
欢迎关注公众号:文本魔术,了解更多
清晰编程: 很高兴能帮到你
m0_60595550: 超级谢谢终于解决了!
普通网友: 大佬的文章写的太精辟了 让我深刻了解了这篇文章的精髓 谢谢大佬分享,希望继续创作优质博文。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
普通网友: 写的很详细,感谢博主的分享。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】
普通网友: 文章结构严谨有条,层次分明,读起来一点也不费劲,让人受益匪浅。【我也写了一些相关领域的文章,希望能够得到博主的指导,共同进步!】