Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Lvlin-Qiuyu/typescript-tutorial
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: wangdoc/typescript-tutorial
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Able to merge. These branches can be automatically merged.
Loading
Showing with 674 additions and 309 deletions.
  1. +2 −2 .github/workflows/wangdoc.yml
  2. +1 −1 chapters.yml
  3. +3 −3 docs/any.md
  4. +1 −1 docs/array.md
  5. +10 −36 docs/assert.md
  6. +1 −1 docs/basic.md
  7. +211 −69 docs/class.md
  8. +32 −3 docs/comment.md
  9. +29 −13 docs/declare.md
  10. +2 −2 docs/decorator-legacy.md
  11. +29 −21 docs/decorator.md
  12. +13 −14 docs/enum.md
  13. +1 −1 docs/es6.md
  14. +42 −10 docs/function.md
  15. +1 −1 docs/generics.md
  16. +8 −8 docs/interface.md
  17. +2 −2 docs/intro.md
  18. +5 −5 docs/mapping.md
  19. +23 −21 docs/module.md
  20. +1 −1 docs/namespace.md
  21. +62 −26 docs/object.md
  22. +89 −21 docs/operator.md
  23. +1 −1 docs/tsc.md
  24. +56 −25 docs/tsconfig.json.md
  25. +26 −10 docs/tuple.md
  26. +16 −4 docs/types.md
  27. +5 −5 docs/utility.md
  28. +2 −2 package.json
4 changes: 2 additions & 2 deletions .github/workflows/wangdoc.yml
Original file line number Diff line number Diff line change
@@ -10,11 +10,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 'latest'
- name: Install dependencies
2 changes: 1 addition & 1 deletion chapters.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@
- decorator-legacy.md: 装饰器(旧语法)
- declare.md: declare 关键字
- d.ts.md: d.ts 类型声明文件
- operator.md: 运算符
- operator.md: 类型运算符
- mapping.md: 类型映射
- utility.md: 类型工具
- comment.md: 注释指令
6 changes: 3 additions & 3 deletions docs/any.md
Original file line number Diff line number Diff line change
@@ -85,7 +85,7 @@ x = { foo: 'hello' };

由于这个原因,建议使用`let``var`声明变量时,如果不赋值,就一定要显式声明类型,否则可能存在安全隐患。

`const`命令没有这个问题,因为 TypeScript 要求`const`声明变量时,必须同时进行初始化(赋值)。
`const`命令没有这个问题,因为 JavaScript 语言规定`const`声明变量时,必须同时进行初始化(赋值)。

```typescript
const x; // 报错
@@ -237,10 +237,10 @@ function f():never {

let v1:number = f(); // 不报错
let v2:string = f(); // 不报错
let v3:string = f(); // 不报错
let v3:boolean = f(); // 不报错
```

上面示例中,函数`f()`会抛错,所以返回值类型可以写成`never`,即不可能返回任何值。各种其他类型的变量都可以赋值为`f()`的运行结果(`never`类型)。
上面示例中,函数`f()`会抛出错误,所以返回值类型可以写成`never`,即不可能返回任何值。各种其他类型的变量都可以赋值为`f()`的运行结果(`never`类型)。

为什么`never`类型可以赋值给任意其他类型呢?这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了`never`类型。因此,`never`类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。

2 changes: 1 addition & 1 deletion docs/array.md
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ let arr:(number|string)[];

上面示例中,数组`arr`的成员类型是`number|string`

这个例子里面的圆括号是必须的,否则因为竖杠`|`的优先级低于`[]`,TypeScript 会把`number|string[]`理解成`number``string[]`的联合类型。
这个例子里面的圆括号是必须的,否则因为竖杠`|`的优先级低于`[]`,TypeScript 会把`number|string[]`理解成`number``string[]`的联合类型。

如果数组成员可以是任意类型,写成`any[]`。当然,这种写法是应该避免的。

46 changes: 10 additions & 36 deletions docs/assert.md
Original file line number Diff line number Diff line change
@@ -114,15 +114,6 @@ const s2:string = value as string; // 正确

上面示例中,unknown 类型的变量`value`不能直接赋值给其他类型的变量,但是可以将它断言为其他类型,这样就可以赋值给别的变量了。

另外,类型断言也适合指定联合类型的值的具体类型。

```typescript
const s1:number|string = 'hello';
const s2:number = s1 as number;
```

上面示例中,变量`s1`是联合类型,可以断言其为联合类型里面的一种具体类型,再将其赋值给变量`s2`

## 类型断言的条件

类型断言并不意味着,可以把某个值断言为任意类型。
@@ -228,8 +219,7 @@ let s = 'JavaScript';
setLang(s as const); // 报错
```

上面示例中,`as
const`断言用于变量`s`,就报错了。下面的写法可以更清晰地看出这一点。
上面示例中,`as const`断言用于变量`s`,就报错了。下面的写法可以更清晰地看出这一点。

```typescript
let s1 = 'JavaScript';
@@ -414,33 +404,26 @@ class Point {
断言函数是一种特殊函数,用于保证函数参数符合某种类型。如果函数参数达不到要求,就会抛出错误,中断程序执行;如果达到要求,就不进行任何操作,让代码按照正常流程运行。

```typescript
function isString(value) {
function isString(value:unknown):void {
if (typeof value !== 'string')
throw new Error('Not a string');
}
```

上面示例中,函数`isString()`就是一个断言函数,用来保证参数`value`是一个字符串。
上面示例中,函数`isString()`就是一个断言函数,用来保证参数`value`是一个字符串,否则就会抛出错误,中断程序的执行

下面是它的用法。

```typescript
const aValue:string|number = 'Hello';
isString(aValue);
```

上面示例中,变量`aValue`可能是字符串,也可能是数组。但是,通过调用`isString()`,后面的代码就可以确定,变量`aValue`一定是字符串。

断言函数的类型可以写成下面这样。

```typescript
function isString(value:unknown):void {
if (typeof value !== 'string')
throw new Error('Not a string');
function toUpper(x: string|number) {
isString(x);
return x.toUpperCase();
}
```

上面代码中,函数参数`value`的类型是`unknown`,返回值类型是`void`,即没有返回值。可以看到,单单从这样的类型声明,很难看出`isString()`是一个断言函数。
上面示例中,函数`toUpper()`的参数`x`,可能是字符串,也可能是数值。但是,函数体的最后一行调用`toUpperCase()`方法,必须保证`x`是字符串,否则报错。所以,这一行前面调用断言函数`isString()`,调用以后 TypeScript 就能确定,变量`x`一定是字符串,不是数值,也就不报错了。

传统的断言函数`isString()`的写法有一个缺点,它的参数类型是`unknown`,返回值类型是`void`(即没有返回值)。单单从这样的类型声明,很难看出`isString()`是一个断言函数。

为了更清晰地表达断言函数,TypeScript 3.7 引入了新的类型写法。

@@ -507,18 +490,9 @@ function assertIsDefined<T>(

上面示例中,工具类型`NonNullable<T>`对应类型`T`去除空类型后的剩余类型。

如果要将断言函数用于函数表达式,可以采用下面的写法。
如果要将断言函数用于函数表达式,可以采用下面的写法。根据 TypeScript 的[要求](https://github.com/microsoft/TypeScript/pull/33622#issuecomment-575301357),这时函数表达式所赋予的变量,必须有明确的类型声明。

```typescript
// 写法一
const assertIsNumber = (
value:unknown
):asserts value is number => {
if (typeof value !== 'number')
throw Error('Not a number');
};

// 写法二
type AssertIsNumber =
(value:unknown) => asserts value is number;

2 changes: 1 addition & 1 deletion docs/basic.md
Original file line number Diff line number Diff line change
@@ -69,7 +69,7 @@ function toString(num:number) {

上面示例中,函数`toString()`没有声明返回值的类型,但是 TypeScript 推断返回的是字符串。正是因为 TypeScript 的类型推断,所以函数返回值的类型通常是省略不写的。

从这里可以看到,TypeScript 的设计思想是,类型声明是可选的,你可以加,也可以不加。即使不加类型声明,依然是有效的 TypeScript 代码,只是这时不能保证 TypeScript 会正确推断出类型。由于这个原因所有 JavaScript 代码都是合法的 TypeScript 代码。
从这里可以看到,TypeScript 的设计思想是,类型声明是可选的,你可以加,也可以不加。即使不加类型声明,依然是有效的 TypeScript 代码,只是这时不能保证 TypeScript 会正确推断出类型。由于这个原因所有 JavaScript 代码都是合法的 TypeScript 代码。

这样设计还有一个好处,将以前的 JavaScript 项目改为 TypeScript 项目时,你可以逐步地为老代码添加类型,即使有些代码没有添加,也不会无法运行。

Loading