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

1、单元类型

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

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

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

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

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类型的值进行任何类型检查,这意味着你可以对该值进行任何操作,而不会引发编译错误。

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类型时,你不能直接对该值进行任何操作,除非你先进行类型断言或类型检查。

  举个例子:

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

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

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

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的一种推断机制,它允许在条件语句中根据条件的结果缩小值的类型范围。举个例子:

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类型的例子:

// 返回 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

  举个例子:

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

 

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

3.3、应用场景(二)

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

  举个例子:

function loop(): never {  
  while (true) {  
    // do something  
  }  
}

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

3.4、应用场景(三)

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

  举个例子:

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

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

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

posted @ 2023-10-04 17:18  左扬  阅读(92)  评论(0编辑  收藏  举报
levels of contents