docs: finish chapter 3
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
- intro.md: 简介
|
||||
- basic.md: 基本用法
|
||||
- any.md: any 类型,unknown 类型,never 类型
|
||||
|
||||
60
docs/any.md
60
docs/any.md
@@ -6,7 +6,7 @@
|
||||
|
||||
### 基本含义
|
||||
|
||||
any 类型表示该位置不限制类型,任意类型的值都可以使用。
|
||||
any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。
|
||||
|
||||
```typescript
|
||||
let x:any;
|
||||
@@ -18,25 +18,32 @@ x = true; // 正确
|
||||
|
||||
上面示例中,变量`x`的类型是`any`,就可以被赋值为任意类型的值。
|
||||
|
||||
变量类型一旦设为`any`,TypeScript 实际上会关闭它的类型检查,即使有明显的类型错误,只要句法正确,都不会报错。
|
||||
变量类型一旦设为`any`,TypeScript 实际上会关闭这个变量的类型检查。即使有明显的类型错误,只要句法正确,都不会报错。
|
||||
|
||||
```typescript
|
||||
let x:any = 'hello';
|
||||
x(1) // 正确
|
||||
x.foo = 100; // 正确
|
||||
|
||||
x(1) // 不报错
|
||||
x.foo = 100; // 不报错
|
||||
```
|
||||
|
||||
上面示例中,变量`x`的值是一个字符串,但是把它当作函数调用,或者当作对象读取任意属性,TypeScript 编译时都不报错。原因就是`x`的类型是`any`,TypeScript 不对其进行类型检查。
|
||||
|
||||
实际项目中,`any`类型往往用于关闭某些变量的类型检查。由于这个原因,应该尽量避免使用`any`类型,否则就失去了使用 TypeScript 的意义。
|
||||
由于这个原因,应该尽量避免使用`any`类型,否则就失去了使用 TypeScript 的意义。
|
||||
|
||||
这个类型的主要设计目的,是为了适配以前老的 JavaScript 项目的迁移。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上`any`,TypeScript 编译时就不会报错。不过,这大概是`any`唯一的适用场合。
|
||||
实际开发中,`any`类型主要适用以下两个场合。
|
||||
|
||||
(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为`any`。
|
||||
|
||||
(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为`any`。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上`any`,TypeScript 编译时就不会报错。
|
||||
|
||||
总之,TypeScript 认为,只要开发者使用了`any`类型,就表示开发者想要自己来处理这些代码,所以就不对`any`类型进行任何限制,怎么使用都可以。
|
||||
|
||||
从集合论的角度看,`any`类型可以看成是所有其他类型的全集,包含了一切可能的类型。TypeScript 将这种类型称为“顶层类型”(top type),意为涵盖了所有下层。
|
||||
|
||||
### 类型推断问题
|
||||
|
||||
`any`类型的另一个出现场景是,对于那些开发者没有指定类型、TypeScript 必须自己推断类型的变量,如果这时无法推断出类型,TypeScript 就会认为该变量的类型是`any`。
|
||||
对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是`any`。
|
||||
|
||||
```typescript
|
||||
function add(x, y) {
|
||||
@@ -46,17 +53,17 @@ function add(x, y) {
|
||||
add(1, [1, 2, 3]) // 正确
|
||||
```
|
||||
|
||||
上面示例中,函数`add()`的参数变量`x`和`y`,都没有足够的信息,TypeScript 无法推断出它们的类型,就会认为这些变量的类型是`any`。以至于后面就不再对函数`add()`进行类型检查了,怎么用都可以。
|
||||
上面示例中,函数`add()`的参数变量`x`和`y`,都没有足够的信息,TypeScript 无法推断出它们的类型,就会认为这两个变量和函数返回值的类型都是`any`。以至于后面就不再对函数`add()`进行类型检查了,怎么用都可以。
|
||||
|
||||
这显然是很糟糕的情况,所以对于那些类型不明显的变量,一定要明确声明类型,防止推断为`any`。
|
||||
这显然是很糟糕的情况,所以对于那些类型不明显的变量,一定要显式声明类型,防止被推断为`any`。
|
||||
|
||||
TypeScript 提供了一个编译选项`--noImplicitAny`,只要打开这个选项,推断不出类型就会报错。
|
||||
TypeScript 提供了一个编译选项`--noImplicitAny`,打开该选项,只要推断出`any`类型就会报错。
|
||||
|
||||
```bash
|
||||
$ tsc --noImplicitAny app.ts
|
||||
```
|
||||
|
||||
上面命令就使用了`--noImplicitAny`编译选项进行编译,这时上面的函数`add()`就会报错。
|
||||
上面命令使用了`--noImplicitAny`编译选项进行编译,这时上面的函数`add()`就会报错。
|
||||
|
||||
### 污染问题
|
||||
|
||||
@@ -72,21 +79,21 @@ y * 123 // 正确
|
||||
y.toFixed() // 正确
|
||||
```
|
||||
|
||||
上面示例中,变量`x`的类型是`any`,实际的值是一个字符串。数值类型的变量`y`被赋值为`x`,也不会报错。然后,变量`y`继续进行各种数值运算,TypeScript 也检查不出错误,问题就这样留到运行时才会暴露。
|
||||
上面示例中,变量`x`的类型是`any`,实际的值是一个字符串。变量`y`的类型是`number`,表示这是一个数值变量,但是它被赋值为`x`,这时并不会报错。然后,变量`y`继续进行各种数值运算,TypeScript 也检查不出错误,问题就这样留到运行时才会暴露。
|
||||
|
||||
污染其他具有正确类型的变量,把错误留到运行时,这就是不宜使用`any`类型的另一个主要原因。
|
||||
|
||||
### 顶端类型
|
||||
### 顶层类型
|
||||
|
||||
前面说过,`any`类型可以被赋值为任何类型的值。在 TypeScript 语言中,如果类型`A`可以被赋值为类型`B`,那么类型`A`称为父类型,类型`B`称为子类型。TypeScript 的一个规则是,凡是可以使用父类型的地方,都可以使用子类型。
|
||||
前面说过,`any`类型可以被赋值为任何类型的值。在 TypeScript 语言中,如果类型`A`可以被赋值为类型`B`,就表示类型`B`具有类型`A`的全部特征,这时就称类型`B`是类型`A`的超类。
|
||||
|
||||
那么类型`A`称为父类型,类型`B`称为子类型。TypeScript 的一个规则是,凡是可以使用父类型的地方,都可以使用子类型。
|
||||
|
||||
由于任何值都可以赋值给`any`类型,所以`any`类型是 TypeScript 所有其他类型的父类型,或者说,所有其他类型都是`any`的子类型。
|
||||
|
||||
所以,`any`类型是 TypeScript 的一个基础类型,包含了一切可能的值。所有其他类型都可以看成是它的衍生类型,它又被称为顶端类型(top type)。
|
||||
|
||||
## unknown 类型
|
||||
|
||||
为了解决`any`类型“污染”其他变量的问题,TypeScript 3.0 引入了[`unknown`类型](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type)。它与`any`含义相同,表示类型不确定,但是使用上有一些限制,可以视为严格版的`any`。
|
||||
为了解决`any`类型“污染”其他变量的问题,TypeScript 3.0 引入了[`unknown`类型](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type)。它与`any`含义相同,表示类型不确定,可能是任意类型,但是它的使用有一些限制,不像`any`那样自由,可以视为严格版的`any`。
|
||||
|
||||
`unknown`跟`any`的相似之处,在于所有类型的值都可以分配给`unknown`类型。
|
||||
|
||||
@@ -113,7 +120,7 @@ let v2:number = v; // 报错
|
||||
|
||||
上面示例中,变量`v`是`unknown`类型,赋值给`any`和`unknown`以外类型的变量都会报错,这就避免了污染问题,从而克服了`any`类型的一大缺点。
|
||||
|
||||
另外,也不能直接调用`unknown`类型变量的方法和属性。
|
||||
其次,不能直接调用`unknown`类型变量的方法和属性。
|
||||
|
||||
```typescript
|
||||
let v1:unknown = { foo: 123 };
|
||||
@@ -167,15 +174,15 @@ if (typeof s === 'string') {
|
||||
|
||||
这样设计的目的是,只有明确`unknown`变量的实际类型,才允许使用它,防止像`any`那样可以随意乱用,“污染”其他变量。类型细化以后再使用,就不会报错。
|
||||
|
||||
总之,`unknown`可以看作是更安全的`any`,凡是需要设为`any`的地方,通常都应该优先考虑设为`unknown`。
|
||||
总之,`unknown`可以看作是更安全的`any`。一般来说,凡是需要设为`any`类型的地方,通常都应该优先考虑设为`unknown`类型。
|
||||
|
||||
由于`unknown`类型的变量也可以被赋值为任意其他类型,所以其他类型(除了`any`)都可以视为它的子类型。所以,它和`any`一样都属于 TypeScript 的顶端类型。
|
||||
在集合论上,`unknown`也可以视为所有其他类型(除了`any`)的全集,所以它和`any`一样,也属于 TypeScript 的顶层类型。
|
||||
|
||||
## never 类型
|
||||
|
||||
类型也可能是空集,即不包含任何类型。为了逻辑的完整性,TypeScript 把这种情况也当作一种类型,叫做`never`类型。
|
||||
为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript 还引入了“空类型”的概念,即该类型为空,不包含任何值。
|
||||
|
||||
`never`类型表示不可能的类型,也就是不可能有任何值属于这个类型。
|
||||
由于不存在任何属于“空类型”的值,所以该类型被称为`never`,即不可能发生这样的值。
|
||||
|
||||
```typescript
|
||||
let x: never;
|
||||
@@ -199,9 +206,9 @@ function fn(x:string|number) {
|
||||
}
|
||||
```
|
||||
|
||||
上面示例中,参数变量`x`可能是字符串,也可能是数组,判断了这两种情况后,剩下的`else`分支里面,`x`就是`never`类型了。
|
||||
上面示例中,参数变量`x`可能是字符串,也可能是数值,判断了这两种情况后,剩下的最后那个`else`分支里面,`x`就是`never`类型了。
|
||||
|
||||
任何类型的变量都可以赋值为`never`类型。
|
||||
`never`类型的一个重要特点是,可以赋值给任意其他类型。
|
||||
|
||||
```typescript
|
||||
function f():never {
|
||||
@@ -215,4 +222,7 @@ let v3:string = f(); // 正确
|
||||
|
||||
上面示例中,函数`f()`会抛错,所以返回值类型可以写成`never`,即不可能返回任何值。各种其他类型的变量都可以赋值为`f()`的运行结果(`never`类型)。
|
||||
|
||||
前面说过,在 TypeScript 中,如果类型`A`可以被赋值为类型`B`,那么类型`B`就称为类型`A`的子类型。所以,`never`类型可以视为所有其他类型的子类型,表示不包含任何可能的值,这种情况叫做“尾端类型”(bottom type),`never`是 TypeScript 唯一的尾端类型。
|
||||
为什么`never`类型可以赋值给任意其他类型呢?这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了`never`类型。因此,`never`类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。
|
||||
|
||||
总之,TypeScript 有两个“顶层类型”(`any`和`unknown`),但是“底层类型”只有`never`唯一一个。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user