前言
ts有两种顶层类型:any
和unknown
,它们非常像,在日常使用中很难界定什么时候该用any
,什么时候该用unknown
。这篇文章就来捋一捋。
any
任何类型都可以分配给any
,它是ts类型检查的逃生舱,告诉tsc直接跳过类型检查。any
违背了类型检查的初衷,一般不建议使用,尤其在有了unknown
类型之后。
unknown
Typescript3.0引入了unknown
,可以理解为类型安全版本的any
。同样地,任何类型都可以分配给unknown
,但是unknown
不可分配给除了unknown
或any
的其他类型,除非有类型断言或者类型收窄的处理。
对比
- 任何类型可以分配给
any
和unknown
,any
可以分配给任何类型,unknown
只能分配给unknown
或者any
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| // 任何类型可以分配给any
function f01<T>(pAny: any, pNever: never, pT: T) {
let x: any;
x = 123;
x = "hello";
x = [1, 2, 3];
x = new Error();
x = x;
x = pAny;
x = pNever;
x = pT;
}
// any可以分配给任何类型
function f02<T>(x: any) {
let v1: any = x;
let v2: unknown = x;
let v3: object = x;
let v4: string = x;
let v5: string[] = x;
let v6: {} = x;
let v7: {} | null | undefined = x;
}
// 任何类型可以分配给unknown
function f21<T>(pAny: any, pNever: never, pT: T) {
let x: unknown;
x = 123;
x = "hello";
x = [1, 2, 3];
x = new Error();
x = x;
x = pAny;
x = pNever;
x = pT;
}
// unknown只能分配给unknown或者any
function f22(x: unknown) {
let v1: any = x;
let v2: unknown = x;
let v3: object = x; // Error
let v4: string = x; // Error
let v5: string[] = x; // Error
let v6: {} = x; // Error
let v7: {} | null | undefined = x; // Error
let v8: string = x as string; // Ok,断言为string
}
function f23(x: unknown): string {
if (typeof x === 'string') {
return x; // Ok
}
return String(x);
}
|
any
与任何类型T
的交叉类型为any
,unknown
与任何类型T
的交叉类型为T
,即
any & T => any unknown & T => T
上面的结论不完全正确,要考虑优先级,never > any > unknown
,所以 any & never => never
,unknown & never => never
,unknown & any => any
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| type T00 = any & null; // any
type T01 = any & undefined; // any
type T02 = any & null & undefined; // never
type T03 = any & string; // any
type T04 = any & string[]; // any
type T05 = any & unknown; // any
type T06 = any & any; // any
type T10 = unknown & null; // null
type T11 = unknown & undefined; // undefined
type T12 = unknown & null & undefined; // null & undefined (which becomes never)
type T13 = unknown & string; // string
type T14 = unknown & string[]; // string[]
type T15 = unknown & unknown; // unknown
type T16 = unknown & any; // any
|
any
与任何类型T
的联合类型为any
,unknown
与任何类型T
的联合类型为unknown
,即
any | T => any |
unknown | T => unknown |
unknown
和any
表现一致,但优先级上any
比较高。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| type T00 = any | null; // any
type T01 = any | undefined; // any
type T02 = any | null | undefined; // any
type T03 = any | string; // any
type T04 = any | string[]; // any
type T05 = any | unknown; // any
type T07 = any | never; // any
type T10 = unknown | null; // unknown
type T11 = unknown | undefined; // unknown
type T12 = unknown | null | undefined; // unknown
type T13 = unknown | string; // unknown
type T14 = unknown | string[]; // unknown
type T16 = unknown | any; // any
type T17 = unknown | never; // unknown
|
any
类型可以属性访问、元素访问、函数调用,unknown
在没有类型收窄时不可以
1
2
3
4
5
6
7
8
9
10
11
12
| function f10(x: any) {
x.foo; // OK
x[5]; // OK
x(); // OK
new x(); // OK
}
function f11(x: unknown) {
x.foo; // Error
x[5]; // Error
x(); // Error
new x(); // Error
}
|
any
可以使用任何操作符和运算符,unknown
只能用相等判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| function f10(x: any) {
x == 5;
x !== 10;
x >= 0;
x + 1;
x * 2;
-x;
+x;
}
function f11(x: unknown) {
x == 5;
x !== 10;
x >= 0; // Error
x + 1; // Error
x * 2; // Error
-x; // Error
+x; // Error
}
|
keyof
的不同
1
2
| type T00 = keyof any; // string | number | symbol
type T01 = keyof unknown; // never
|
- 同态映射的不同
1
2
3
| type T10<T> = { [P in keyof T]: number };
type T11 = T10<any>; // { [x: string]: number }
type T12 = T10<unknown>; // {}
|
- 函数返回的不同。函数返回值类型为
any
时,可以没有return语句,但是为unknown
时要有。
1
2
| function f10(): any {} // OK
function f11(): unknown {} // Error
|
除了上面几种情况,unknown
和any
表现基本一样。
参考资料
- https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html
- https://thesoftwaresimpleton.com/blog/2019/05/26/ts-bottom-type
- https://fullstackbb.com/typescript/typescript-unknown-as-top-type/