TypeScript 特殊类型:any、unknown 和 never

类型系统概述

在 TypeScript 的类型系统中,anyunknownnever 是三个特殊的类型,它们各自具有独特的特性和使用场景。从集合论的角度来看:

  • anyunknown 是顶层类型(top type),可以包含所有其他类型
  • never 是底层类型(bottom type),不包含任何值

any 类型

基本特性

any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。从集合论的角度看,any 类型是所有其他类型的全集。

1
2
3
4
5
6
7
let x: any;

x = 1;        // 正确
x = "hello";  // 正确
x = false;    // 正确
x = [];       // 正确
x = {};       // 正确

类型检查关闭

当变量类型设为 any 时,TypeScript 会关闭该变量的类型检查:

1
2
3
4
5
let x: any = 'hello';

x(1)          // 不报错
x.foo = 100;  // 不报错
x.bar();      // 不报错

类型污染

any 类型存在污染问题,可以赋值给其他任何类型:

1
2
3
4
5
6
let x: any = 'hello';
let y: number;

y = x;        // 不报错
y * 123;      // 不报错
y.toFixed();  // 不报错

使用场景

  1. 特殊情况下关闭类型检查

    1
    2
    
    // 处理第三方库的类型问题
    const thirdPartyLib: any = require('some-library');
    
  2. JavaScript 项目迁移

    1
    2
    
    // 迁移旧项目时临时使用
    const legacyData: any = getLegacyData();
    
  3. 动态数据处理

    1
    2
    
    // 处理动态 API 响应
    const response: any = await fetchData();
    

unknown 类型

基本特性

unknown 是 TypeScript 3.0 引入的类型,可以视为严格版的 any。它表示类型不确定,可能是任意类型,但使用上有严格限制。

1
2
3
4
5
let x: unknown;

x = true;     // 正确
x = 42;       // 正确
x = 'Hello';  // 正确

使用限制

  1. 不能赋值给其他类型

    1
    2
    3
    4
    5
    
    let v: unknown = 123;
    
    let v1: boolean = v;  // 报错
    let v2: number = v;   // 报错
    let v3: string = v;   // 报错
    
  2. 不能直接访问属性和方法

    1
    2
    3
    4
    5
    6
    7
    8
    
    let v1: unknown = { foo: 123 };
    v1.foo;              // 报错
    
    let v2: unknown = 'hello';
    v2.trim();           // 报错
    
    let v3: unknown = (n = 0) => n + 1;
    v3();                // 报错
    

允许的操作

unknown 类型变量只能进行以下操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let a: unknown = 1;

// 允许的操作
a === 1;      // 比较运算
a !== 1;      // 比较运算
!a;           // 取反运算
a && b;       // 逻辑运算
a || b;       // 逻辑运算
a ? b : c;    // 条件运算
typeof a;     // typeof 运算符
a instanceof Date;  // instanceof 运算符

// 不允许的操作
a + 1;        // 报错
a.toString(); // 报错

类型收窄

使用类型收窄后,可以安全地使用 unknown 类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function processValue(value: unknown) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase());  // 正确
  }
  
  if (value instanceof Date) {
    console.log(value.getTime());      // 正确
  }
  
  if (Array.isArray(value)) {
    console.log(value.length);         // 正确
  }
}

never 类型

基本特性

never 类型表示永远不会发生的类型,不包含任何值。从集合论的角度看,它是空集。

1
2
let x: never;     // 正确
let a: never = 1; // 报错

使用场景

  1. 永不返回的函数

    1
    2
    3
    
    function throwError(): never {
      throw new Error('Error');
    }
    
  2. 穷尽性检查

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    type Shape = 'circle' | 'square';
    
    function getArea(shape: Shape): number {
      switch (shape) {
        case 'circle':
          return Math.PI * radius * radius;
        case 'square':
          return side * side;
        default:
          const _exhaustiveCheck: never = shape;
          return _exhaustiveCheck;
      }
    }
    
  3. 类型收窄

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    function processValue(value: string | number) {
      if (typeof value === 'string') {
        // value 被收窄为 string 类型
      } else if (typeof value === 'number') {
        // value 被收窄为 number 类型
      } else {
        // value 被收窄为 never 类型
      }
    }
    

最佳实践

  1. 避免使用 any

    • 优先使用具体的类型
    • 如果必须使用,添加 // @ts-ignore 注释说明原因
  2. 使用 unknown 替代 any

    • 处理外部数据时使用 unknown
    • 通过类型收窄安全地使用
  3. 合理使用 never

    • 用于表示不可能的情况
    • 帮助 TypeScript 进行穷尽性检查

总结

类型描述特点使用场景
any任意类型关闭类型检查,可污染其他类型旧项目迁移,第三方库集成
unknown未知类型类型安全,需要类型收窄处理外部数据,API 响应
never空类型不包含任何值穷尽性检查,永不返回的函数

记住:

  • anyunknown 是顶层类型,可以承接任何类型
  • never 是底层类型,可以赋值给任何类型
  • 优先使用 unknown 而不是 any
  • 使用 never 进行穷尽性检查

57.12k 字
43篇文章