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
类型时,该变量可以持有任何类型的值,包括number
、string
、boolean
、object
、null
、undefined
等。在编译时,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中的主要应用场景是表示一个永远不会出现的值的类型,主要用于异常处理、无限循环和不可达的代码。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!