TypeScript中的 any 和 unknown

184 阅读5分钟

相同点

any 和 unknown 都是 Top Type

可以接收任何值作为他们的参数

比如:

let a: any = "asdasddsa" || 112 || {asds:2} || Symbol()
//...其他任何类型
  
let b: unknown = "asdasddsa" || 112 || {asds:2} || Symbol()
//...其他任何类型

就说明两个类型都是包容万物,就像是我们平时写 JavaScript 一样,一个变量你想要它在什么时候等于什么值都是可以的

let a: any = "asdasddsa" || 112 || {asds:2} || Symbol()
  
let b: unknown = "asdasddsa" || 112 || {asds:2} || Symbol()

a = Symbol()
b = Symbol() 

区别

1、any 类型的变量将会跳过类型检测,但是 unknown 不会

2、any 类型 可以赋值给任何类型,任何类型也可以赋值给 any 类型

3、unknown 类型 只能赋值给 unknown 和 any 类型,任何值都可以赋给unknown 类型

4、any 类型和任何类型的交集,都是any类型

type Z1 = any & 1            // any
type Z2 = any & '1'          // any
type Z3 = any & {a:1}        // any
type Z4 = any & unknown      // any
type Z5 = any & null         // any
type Z6 = any & undefined    // any

type Z1 = any | 1            // any
type Z2 = any | '1'          // any
type Z3 = any | {a:1}        // any
type Z4 = any | unknown      // any
type Z5 = any | null         // any
type Z6 = any | undefined    // any

5、unknown和任何类型的并集都是unknown,除了 any

type Z1 = unknown | 1            // unknown
type Z2 = unknown | '1'          // unknown
type Z3 = unknown | {a:1}        // unknown
type Z4 = unknown | unknown      // unknown
type Z5 = unknown | null         // unknown
type Z6 = unknown | undefined    // unknown
type Z7 = unknown | any          // any

type Z1 = unknown & 1            // 1
type Z2 = unknown & '1'          // '1'
type Z3 = unknown & {a:1}        // {a:1}
type Z4 = unknown & unknown      // unknown
type Z5 = unknown & null         // null
type Z6 = unknown & undefined    // undefined
type Z7 = unknown & any          // any

type Z7 = null & undefined       // never 

两者不同的地方在于, 被赋予 any 类型的变量将会跳过类型检测,但是 unknown 不会

any 类型 我们可以简单的给一个定义:“I don’t care”,简单的说就是我不在意,因为any 类型并不会经过 类型检查器,所以它毫不在意你给它的是什么类型,或者说它要用在什么地方

unknown 类型 我们也可以简单的给一个定义:“I don’t know”,简单的说就是我不知道,作为和 any 类型一样的 Top Type ,我们可以给unknown 类型赋值任何类型,但是你要是想要拿它来做一些事的时候,对不起,他都是不允许的,因为,它不知道啊,它不知道它能不能完成你交给它的任务,所以,它就会给出报错

any 类型 可以赋值给任何类型,任何类型也可以赋值给 any 类型

unknown 类型 只能赋值给 unknown 和 any 类型,任何值都可以赋给unknown 类型

打个比方:

let any1:any
let str:string = '2'
  
any1=str
str=any1 

any 类型的随便赋值都是可以实现的,但是看unknown 类型

let str:string = '2'

let unknown1:unknown

unknown1=str

str=unknown1 //err:Type 'unknown' is not assignable to type 'string'. 

编译就会给出报错,字符串不能够赋值一个unknown 类型的值,因为编译器它不知道你能不能赋值给字符串,所以给出了报错。

那么上面那段代码,要怎么才能够通过编译呢,很简单,它不知道它是什么类型,就要由程序员来告诉它:

let str:string = '2'
let unknown1:unknown

unknown1=str 

if(typeof unknown1 == 'string'){
  str=unknown1 //ok
} 

我们可以使用类型守卫,或者是其他的任何方式,只要是你能够满足你要执行的代码的约束,比如说你要赋值给字符串,那你就一定也要是一个字符串,你要调用,那你就得是一个方法,以此类推

这样做之后,虽然我们用了 Top Type 但是还是能够保证写出来的代码足够安全,起码不是产生那种,传一个字符串却拿来当方法调用这种情况

体操一下

前面介绍了两种类型,那么来做一些简单的体操,第一个是,我们要怎么判断一个类型它是否是 any 类型。第二个,我们要怎么判断一个类型是否是 unknown 类型

IsAny

这里我们要用到一个 any 类型 的特性,那就是any 类型和任何类型的交集,都是any类型

type Z1 = any & 1            // any
type Z2 = any & '1'          // any
type Z3 = any & {a:1}        // any
type Z4 = any & unknown      // any
type Z5 = any & null         // any
type Z6 = any & undefined    // any 

当然这是只有 any 才满足的一个特性,那么我们就可以利用这个特性来判断,一个数它是不是 any

type IsAny <T> = 0 extends 1&T ? true: false

type W1 = IsAny<undefined> // false
type W2 = IsAny<any>       // true 

只有当传入的 泛型 T 是 any 的时候,他才能够满足后面为 any ,这个条件类型的 0 或者 1 并不重要,重要的是,只有当 T 是 any 的时候,前后才能够满足,这样我们就能够判断一个类型是否是 any。

IsUnknown

我最开始自己思考这个问题的时候,发现了 unknown 的一个特性,就是unknown和任何类型的并集都是unknown,除了 any:

type Z1 = unknown | 1            // unknown
type Z2 = unknown | '1'          // unknown
type Z3 = unknown | {a:1}        // unknown
type Z4 = unknown | unknown      // unknown
type Z5 = unknown | null         // unknown
type Z6 = unknown | undefined    // unknown
type Z7 = unknown | any          // any 

那么我们是不是可以按照上上面的思路,最后只要排除掉 any 就好了。

这里还有一个知识点,就是 keyof any 是 string | number | symbol 而 keyof unknown 是 never 我们可以以此来判断

type IsUnknown <T> = 0 extends 1|T ? 
  keyof T extends never? true: false
  : false

type e = IsUnknown<1> 

后面发现我想的太麻烦了,我们之前说过 unknown 类型却不能赋值给 unknown 和 any 以外的任何类型,所以我们可以用这点来优化一下

type IsNotAny<T> = 0 extends 1 & T ? false : true ;
  
type IsUnknown<T> = unknown extends T ? IsNotAny<T> : false;
  
type Test1 = IsUnknown<any>     // false
type Test2 = IsUnknown<unknown> // true 

通过判断传入的类型是不是作为unknown 的父集,这样就能够把范围缩小到 unknown 和 any,再通过上一个判断是否是 any 的工具类型,就能够更简单的实现了。