TypeScript入门到精通——TypeScript类型系统基础——单元类型、顶端类型、尾端类型

1、单元类型

  单元类型(Unit Type)也叫作单例类型(Singleton Type),指的是仅包含一个可能值的类型。由于这个特殊的性质,编译器在处理单元类型时甚至不需要关注单元类型表示的具体值。

  TypeScript 中的单元类型有以下几种:

    • undefined 类型
    • null 类型
    • unique symbol 类型
    • void 类型
    • 字面量类型
    • 联合枚举成员类型

  我们能看到这些单元类型均值包含一个可能值。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x: undefined;
 
let y: null = null;
 
const sym: unique symbol = Symbol();
 
function doSomething(): void { 
  // 函数体 
}
 
const str: "hello" = "hello";
 
enum Shape { 
  Circle = "circle"
  Square = "square"
  Triangle = "triangle" 
   
function drawShape(shape: Shape): void { 
  // 根据 shape 的值绘制不同的形状 
}

2、顶端类型

  顶端类型(Top Type)源自于数学中的类型论,同时它也被广泛应用于计算机编程语言中。顶端类型是一种通用类型,有时也称为通用超类型,因为在类型系统中,所有类型都是顶端类型的子类型,或者说顶端类型是所有其他类型的父类型。顶端类型涵盖了类型系统中所有可能的值。

  TypeScript 中有以下两种顶端类型:

    • any
    • unknown

2.1、any

  any类型是TypeScript中的一个顶级类型,它表示任意类型的值。当你将一个变量声明为any类型时,该变量可以持有任何类型的值,包括numberstringbooleanobjectnullundefined等。在编译时,TypeScript不会对any类型的值进行任何类型检查,这意味着你可以对该值进行任何操作,而不会引发编译错误。

1
2
3
4
let value: any = "Hello, world!"
value = 42; // 合法,因为value的类型是any 
value = { name: "John" }; // 合法,因为value的类型是any 
value.nonExistentProperty = true; // 合法,因为value的类型是any

  尽管any类型在某些情况下很有用,但它会禁用TypeScript的类型检查功能,可能导致运行时错误。因此,应该尽量避免在代码中过度使用any类型,而是使用更具体的类型。

2.2、unknown 类型

  unknown类型是TypeScript 3.0中引入的一个顶级类型,它表示未知类型的值。与any类型不同,当你将一个变量声明为unknown类型时,你不能直接对该值进行任何操作,除非你先进行类型断言或类型检查。

  举个例子:

1
2
3
4
let value: unknown = "Hello, world!"
value = 42; // 合法,因为value的类型是unknown 
value = { name: "John" }; // 合法,因为value的类型是unknown 
value.nonExistentProperty = true; // 编译错误,因为value的类型是unknown

  unknown类型提供了一种更安全的方式来处理不确定类型的值。在进行操作之前,你需要先确定值的实际类型,这可以通过类型断言或类型检查来实现。例如,你可以使用typeof运算符或instanceof`运算符来检查值的类型,或者使用类型断言来指定值的类型。

  下面是一个使用unknown类型的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
function handleValue(value: unknown) { 
  if (typeof value === "string") { 
    console.log(`String value: ${value}`); 
  } else if (typeof value === "number") { 
    console.log(`Number value: ${value}`); 
  } else
    console.log(`Unknown value: ${value}`); 
  
   
handleValue("Hello, world!"); // 输出:String value: Hello, world! 
handleValue(42); // 输出:Number value: 42 
handleValue({ name: "John" }); // 输出:Unknown value: [object Object]  

在这个例子中,handleValue函数的参数被声明为unknown类型。在函数体内,我们使用typeof运算符来检查值的类型,并根据不同的类型执行相应的操作。

2.3、类型安全性

    为什么有了 any 还要有 unknown 呢?

  any类型在编译时会禁用 TypeScript 的类型检查功能,这意味着你可以对any类型的值进行任何操作,而不会引发编译错误。然而,这也可能导致运行时错误。相比之下,unknown类型在编译时保持了类型安全性。如果你尝试对unknown类型的值进行任何操作,编译器会抛出错误,除非你先进行类型断言或类型检查。

  在TypeScript中,使用unknown类型可以利用逐步类型细化(Type Narrowing)的特性。逐步类型细化是TypeScript的一种推断机制,它允许在条件语句中根据条件的结果缩小值的类型范围。举个例子:

1
2
3
4
5
6
7
8
9
10
11
function handleValue(value: unknown) { 
  if (typeof value === "string") { 
    // 在这个分支中,value 的类型被细化为 string 
    console.log(`String value: ${value.toUpperCase()}`); // 这里可以使用 string 类型的方法 
  } else if (typeof value === "number") { 
    // 在这个分支中,value 的类型被细化为 number 
    console.log(`Number value: ${value * 2}`); // 这里可以使用 number 类型的方法 
  } else
    console.log(`Unknown value: ${value}`); 
  
}

  在这个例子中,handleValue函数的参数被声明为unknown类型。在函数体内,我们使用typeof运算符来检查值的类型,并根据不同的类型执行相应的操作。由于逐步类型细化的特性,当进入某个条件分支时,我们可以确定value的具体类型,并且可以使用相应类型的方法。  

  总结:

    • any类型在编译时禁用了TypeScript的类型检查功能,可能导致运行时错误。它适用于一些特殊情况,但在大多数情况下应该避免使用。
    • unknown类型在编译时保持了类型安全性,并且可以利用逐步类型细化的特性。它适用于处理不确定类型的值,并且可以根据需要进行类型检查和类型断言。使用unknown类型可以提供更好的代码质量和类型安全性。

3、尾端效应

  在类型系统中,尾端类型(Bottom Type)是所有其他类型的子类型。由于一个值不可能同时属于所有类型,例如一个值不可能同时为数字类型和字符串类型,因此尾端类型中不包含任何值。尾端类型也称为 0 类型或者空之类。

3.1、never

  在 TypeScript 中,never类型是一个特殊的类型,它表示的是一个永远不会出现的值。这意味着,如果一个函数永远不会返回(例如,总是抛出错误),那么它的返回类型就是never

  下面是一些never类型的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 返回 never 的函数 
function error(message: string): never { 
    throw new Error(message); 
   
// inferred type is never 
let x = (() => { throw new Error("oops"); })(); 
   
// 在这个例子中,我们有一个联合类型 `string | number`,但是我们有一个条件 `x is never`,这个条件永远是 false,所以 `x` 的类型被推断为 `never`。 
let x: string | number; 
if (x is never) { 
    // 这个代码块永远不会执行 
    console.log("x is never"); 
} else
    console.log("x is not never"); 
}

  never类型在 TypeScript 的类型系统中扮演了很重要的角色,尤其是在处理一些复杂的类型推断和条件类型时。

3.2、应用场景(一)

  异常处理:在函数中,如果有一个分支是永远不可能被执行到的,那么这个分支的返回类型就可以被推断为never

  举个例子:

1
2
3
function error(message: string): never { 
  throw new Error(message); 
}

 

这个函数永远不会返回一个有效的值,它只会抛出一个错误。因此,它的返回类型是 never。  

3.3、应用场景(二)

  无限循环:一个永远不会结束的循环的返回类型也可以是 never

  举个例子:

1
2
3
4
5
function loop(): never { 
  while (true) { 
    // do something 
  
}

这个函数将永远不会返回,因此它的返回类型是 never。  

3.4、应用场景(三)

  在TypeScript中,有一些代码是永远不会被执行到的,例如在一个return语句之后的代码。这些代码的返回类型也可以是never

  举个例子:

1
2
3
4
function unreachableCode(): never { 
  return
  console.log("This code is unreachable"); // Error: Unreachable code detected. 
}

  这个函数中的console.log语句是永远不会被执行到的,因此它的返回类型是never

  总的来说,never类型在TypeScript中的主要应用场景是表示一个永远不会出现的值的类型,主要用于异常处理、无限循环和不可达的代码。

posted @   左扬  阅读(126)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
levels of contents
点击右上角即可分享
微信分享提示