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: ls-serendipity/typescript-tutorial-ryf
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.

Commits on Nov 21, 2023

  1. docs(decorator): fixed wangdoc#84

    ruanyf committed Nov 21, 2023
    Copy the full SHA
    8ad5fc6 View commit details

Commits on Nov 23, 2023

  1. docs(enum): fix wangdoc#85

    ruanyf committed Nov 23, 2023
    Copy the full SHA
    68962e8 View commit details

Commits on Nov 29, 2023

  1. Copy the full SHA
    a30b944 View commit details
  2. Merge pull request wangdoc#88 from jiuyehengxia/mbug-20231129-111111-lx

    fix: delete Superfluous words
    ruanyf authored Nov 29, 2023
    Copy the full SHA
    54f0fd9 View commit details

Commits on Nov 30, 2023

  1. Copy the full SHA
    abad608 View commit details
  2. Merge pull request wangdoc#89 from hermiablog/main

    修改对象类型中属性名的索引类型中的语义错误
    ruanyf authored Nov 30, 2023
    Copy the full SHA
    780059c View commit details

Commits on Dec 7, 2023

  1. docs(decorator): fix example

    ruanyf committed Dec 7, 2023
    Copy the full SHA
    d8b2606 View commit details

Commits on Dec 11, 2023

  1. docs(declare): fixed wangdoc#91

    ruanyf committed Dec 11, 2023
    Copy the full SHA
    f77f8d1 View commit details

Commits on Dec 13, 2023

  1. docs: fixed wangdoc#92

    ruanyf committed Dec 13, 2023
    Copy the full SHA
    eb048a3 View commit details
  2. Copy the full SHA
    838b2f6 View commit details

Commits on Dec 16, 2023

  1. docs(declare): fixed error

    ruanyf committed Dec 16, 2023
    Copy the full SHA
    d21f77e View commit details
  2. docs: fixed typos

    ruanyf committed Dec 16, 2023
    Copy the full SHA
    ef7d619 View commit details

Commits on Dec 21, 2023

  1. docs(decorator): fixed wangdoc#93

    ruanyf committed Dec 21, 2023
    Copy the full SHA
    68da0fb View commit details

Commits on Dec 26, 2023

  1. docs(interface): fixed typo

    ruanyf committed Dec 26, 2023
    Copy the full SHA
    0299aa9 View commit details

Commits on Jan 9, 2024

  1. docs(module): fixed wangdoc#96

    ruanyf committed Jan 9, 2024
    Copy the full SHA
    84474db View commit details

Commits on Mar 18, 2024

  1. docs(assert): fixed wangdoc#100

    ruanyf committed Mar 18, 2024
    Copy the full SHA
    4c7a0cb View commit details

Commits on Mar 19, 2024

  1. docs(operator): fixed wangdoc#101

    ruanyf committed Mar 19, 2024
    Copy the full SHA
    5d4a2f1 View commit details

Commits on Mar 21, 2024

  1. docs(function): fixed wangdoc#102

    ruanyf committed Mar 21, 2024
    Copy the full SHA
    d5e0f47 View commit details

Commits on Apr 10, 2024

  1. docs(function): fixed typo

    ruanyf committed Apr 10, 2024
    Copy the full SHA
    57cdc82 View commit details

Commits on May 23, 2024

  1. Copy the full SHA
    ae9bea9 View commit details

Commits on Jun 8, 2024

  1. refactor: update dependencies

    ruanyf committed Jun 8, 2024
    Copy the full SHA
    1605a24 View commit details
  2. Copy the full SHA
    22904d3 View commit details
  3. docs(utility): fixed wangdoc#107

    ruanyf committed Jun 8, 2024
    Copy the full SHA
    f665b3f View commit details

Commits on Jun 9, 2024

  1. docs(utility): edit text

    ruanyf committed Jun 9, 2024
    Copy the full SHA
    73e9fdc View commit details

Commits on Jun 22, 2024

  1. docs(object): fixed wangdoc#109

    ruanyf committed Jun 22, 2024
    Copy the full SHA
    0666054 View commit details

Commits on Jun 27, 2024

  1. docs(class):correction words

    "MotoVehicle" -> "MotorVehicle"
    LOUSANPANG authored Jun 27, 2024
    Copy the full SHA
    a7d9796 View commit details
  2. Merge pull request wangdoc#1 from LOUSANPANG/LOUSANPANG-patch-1

    docs(class):correction words
    LOUSANPANG authored Jun 27, 2024
    Copy the full SHA
    3a64f73 View commit details

Commits on Jun 28, 2024

  1. Merge pull request wangdoc#111 from LOUSANPANG/main

    docs(class):correction words
    ruanyf authored Jun 28, 2024
    Copy the full SHA
    8d73b03 View commit details

Commits on Jul 1, 2024

  1. Copy the full SHA
    bbde468 View commit details
  2. Copy the full SHA
    8f59ef8 View commit details

Commits on Aug 2, 2024

  1. Copy the full SHA
    dd06fc5 View commit details

Commits on Sep 23, 2024

  1. docs(assert): fixed wangdoc#119

    ruanyf committed Sep 23, 2024
    Copy the full SHA
    b376397 View commit details
  2. Copy the full SHA
    f68cdbc View commit details

Commits on Sep 29, 2024

  1. docs(enum): fixed wangdoc#122

    ruanyf committed Sep 29, 2024
    Copy the full SHA
    5c56fd5 View commit details

Commits on Oct 3, 2024

  1. Copy the full SHA
    e7e1a77 View commit details
  2. docs(declare): edit text

    ruanyf committed Oct 3, 2024
    Copy the full SHA
    5361b8c View commit details

Commits on Oct 12, 2024

  1. Copy the full SHA
    635a5fa View commit details

Commits on Oct 15, 2024

  1. Copy the full SHA
    1725cbb View commit details

Commits on Dec 31, 2024

  1. docs(operator): fixed typo

    ruanyf committed Dec 31, 2024
    Copy the full SHA
    1f5e929 View commit details
Showing with 283 additions and 137 deletions.
  1. +2 −2 .github/workflows/wangdoc.yml
  2. +1 −19 docs/assert.md
  3. +163 −45 docs/class.md
  4. +1 −1 docs/comment.md
  5. +21 −6 docs/declare.md
  6. +24 −16 docs/decorator.md
  7. +4 −5 docs/enum.md
  8. +3 −1 docs/function.md
  9. +1 −1 docs/interface.md
  10. +4 −2 docs/module.md
  11. +2 −3 docs/object.md
  12. +11 −12 docs/operator.md
  13. +40 −18 docs/tsconfig.json.md
  14. +4 −4 docs/utility.md
  15. +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
20 changes: 1 addition & 19 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`

## 类型断言的条件

类型断言并不意味着,可以把某个值断言为任意类型。
@@ -499,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;

208 changes: 163 additions & 45 deletions docs/class.md
Original file line number Diff line number Diff line change
@@ -471,7 +471,7 @@ interface Swimmable {
// ...
}

interface SuperCar extends MotoVehicle,Flyable, Swimmable {
interface SuperCar extends MotorVehicle,Flyable, Swimmable {
// ...
}

@@ -570,7 +570,7 @@ const c1:Car = new Car();
const c2:MotorVehicle = new Car();
```

上面示例中,变量的类型可以写成类`Car`,也可以写成接口`MotorVehicle`。它们的区别是,如果类`Car`有接口`MotoVehicle`没有的属性和方法,那么只有变量`c1`可以调用这些属性和方法。
上面示例中,变量的类型可以写成类`Car`,也可以写成接口`MotorVehicle`。它们的区别是,如果类`Car`有接口`MotorVehicle`没有的属性和方法,那么只有变量`c1`可以调用这些属性和方法。

作为类型使用时,类名只能表示实例的类型,不能表示类的自身类型。

@@ -954,64 +954,47 @@ class Test extends getGreeterBase() {

上面示例中,例一和例二的`extends`关键字后面都是构造函数,例三的`extends`关键字后面是一个表达式,执行后得到的也是一个构造函数。

对于那些只设置了类型、没有初值的顶层属性,有一个细节需要注意。
## override 关键字

```typescript
interface Animal {
animalStuff: any;
}

interface Dog extends Animal {
dogStuff: any;
}

class AnimalHouse {
resident: Animal;
子类继承父类时,可以覆盖父类的同名方法。

constructor(animal:Animal) {
this.resident = animal;
```typescript
class A {
show() {
// ...
}
hide() {
// ...
}
}

class DogHouse extends AnimalHouse {
resident: Dog;

constructor(dog:Dog) {
super(dog);
class B extends A {
show() {
// ...
}
hide() {
// ...
}
}
```

上面示例中,类`DogHouse`的顶层成员`resident`只设置了类型(`Dog`),没有设置初值。这段代码在不同的编译设置下,编译结果不一样。

如果编译设置的`target`设成大于等于`ES2022`,或者`useDefineForClassFields`设成`true`,那么下面代码的执行结果是不一样的。

```typescript
const dog = {
animalStuff: 'animal',
dogStuff: 'dog'
};

const dogHouse = new DogHouse(dog);

console.log(dogHouse.resident) // undefined
```

上面示例中,`DogHouse`实例的属性`resident`输出的是`undefined`,而不是预料的`dog`。原因在于 ES2022 标准的 Class Fields 部分,与早期的 TypeScript 实现不一致,导致子类的那些只设置类型、没有设置初值的顶层成员在基类中被赋值后,会在子类被重置为`undefined`,详细的解释参见《tsconfig.json》一章,以及官方 3.7 版本的[发布说明](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier)
上面示例中,B 类定义了自己的`show()`方法和`hide()`方法,覆盖了 A 类的同名方法。

解决方法就是使用`declare`命令,去声明顶层成员的类型,告诉 TypeScript 这些成员的赋值由基类实现
但是有些时候,我们继承他人的类,可能会在不知不觉中,就覆盖了他人的方法。为了防止这种情况,TypeScript 4.3 引入了 [override 关键字](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#override-and-the---noimplicitoverride-flag)

```typescript
class DogHouse extends AnimalHouse {
declare resident: Dog;

constructor(dog:Dog) {
super(dog);
class B extends A {
override show() {
// ...
}
override hide() {
// ...
}
}
```

上面示例中,`resident`属性的类型声明前面用了`declare`命令,这样就能确保在编译目标大于等于`ES2022`时(或者打开`useDefineForClassFields`时),代码行为正确。
上面示例中,B 类的`show()`方法和`hide()`方法前面加了 override 关键字,明确表明作者的意图,就是要覆盖 A 类里面的这两个同名方法。这时,如果 A 类没有定义自己的`show()`方法和`hide()`方法,就会报错。

但是,这依然没有解决,子类无意中覆盖父类同名方法的问题。因此,TypeScript 又提供了一个编译参数`noImplicitOverride`。一旦打开这个参数,子类覆盖父类的同名方法就会报错,除非使用了 override 关键字。

## 可访问性修饰符

@@ -1278,6 +1261,140 @@ class A {
}
```

## 顶层属性的处理方法

对于类的顶层属性,TypeScript 早期的处理方法,与后来的 ES2022 标准不一致。这会导致某些代码的运行结果不一样。

类的顶层属性在 TypeScript 里面,有两种写法。

```typescript
class User {
// 写法一
age = 25;

// 写法二
constructor(private currentYear: number) {}
}
```

上面示例中,写法一是直接声明一个实例属性`age`,并初始化;写法二是顶层属性的简写形式,直接将构造方法的参数`currentYear`声明为实例属性。

TypeScript 早期的处理方法是,先在顶层声明属性,但不进行初始化,等到运行构造方法时,再完成所有初始化。

```typescript
class User {
age = 25;
}

// TypeScript 的早期处理方法
class User {
age: number;

constructor() {
this.age = 25;
}
}
```

上面示例中,TypeScript 早期会先声明顶层属性`age`,然后等到运行构造函数时,再将其初始化为`25`

ES2022 标准里面的处理方法是,先进行顶层属性的初始化,再运行构造方法。这在某些情况下,会使得同一段代码在 TypeScript 和 JavaScript 下运行结果不一致。

这种不一致一般发生在两种情况。第一种情况是,顶层属性的初始化依赖于其他实例属性。

```typescript
class User {
age = this.currentYear - 1998;

constructor(private currentYear: number) {
// 输出结果将不一致
console.log('Current age:', this.age);
}
}

const user = new User(2023);
```

上面示例中,顶层属性`age`的初始化值依赖于实例属性`this.currentYear`。按照 TypeScript 的处理方法,初始化是在构造方法里面完成的,会输出结果为`25`。但是,按照 ES2022 标准的处理方法,初始化在声明顶层属性时就会完成,这时`this.currentYear`还等于`undefined`,所以`age`的初始化结果为`NaN`,因此最后输出的也是`NaN`

第二种情况与类的继承有关,子类声明的顶层属性在父类完成初始化。

```typescript
interface Animal {
animalStuff: any;
}

interface Dog extends Animal {
dogStuff: any;
}

class AnimalHouse {
resident: Animal;

constructor(animal:Animal) {
this.resident = animal;
}
}

class DogHouse extends AnimalHouse {
resident: Dog;

constructor(dog:Dog) {
super(dog);
}
}
```

上面示例中,类`DogHouse`继承自`AnimalHouse`。它声明了顶层属性`resident`,但是该属性的初始化是在父类`AnimalHouse`完成的。不同的设置运行下面的代码,结果将不一致。

```typescript
const dog = {
animalStuff: 'animal',
dogStuff: 'dog'
};

const dogHouse = new DogHouse(dog);

console.log(dogHouse.resident) // 输出结果将不一致
```

上面示例中,TypeScript 的处理方法,会使得`resident`属性能够初始化,所以输出参数对象的值。但是,ES2022 标准的处理方法是,顶层属性的初始化先于构造方法的运行。这使得`resident`属性不会得到赋值,因此输出为`undefined`

为了解决这个问题,同时保证以前代码的行为一致,TypeScript 从3.7版开始,引入了编译设置`useDefineForClassFields`。这个设置设为`true`,则采用 ES2022 标准的处理方法,否则采用 TypeScript 早期的处理方法。

它的默认值与`target`属性有关,如果输出目标设为`ES2022`或者更高,那么`useDefineForClassFields`的默认值为`true`,否则为`false`。关于这个设置的详细说明,参见官方 3.7 版本的[发布说明](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#the-usedefineforclassfields-flag-and-the-declare-property-modifier)

如果希望避免这种不一致,让代码在不同设置下的行为都一样,那么可以将所有顶层属性的初始化,都放到构造方法里面。

```typescript
class User {
age: number;

constructor(private currentYear: number) {
this.age = this.currentYear - 1998;
console.log('Current age:', this.age);
}
}

const user = new User(2023);
```

上面示例中,顶层属性`age`的初始化就放在构造方法里面,那么任何情况下,代码行为都是一致的。

对于类的继承,还有另一种解决方法,就是使用`declare`命令,去声明子类顶层属性的类型,告诉 TypeScript 这些属性的初始化由父类实现。

```typescript
class DogHouse extends AnimalHouse {
declare resident: Dog;

constructor(dog:Dog) {
super(dog);
}
}
```

上面示例中,`resident`属性的类型声明前面用了`declare`命令。这种情况下,这一行代码在编译成 JavaScript 后就不存在,那么也就不会有行为不一致,无论是否设置`useDefineForClassFields`,输出结果都是一样的。

## 静态成员

类的内部可以使用`static`关键字,定义静态成员。
@@ -1595,4 +1712,5 @@ class FileSystemObject {
## 参考链接

- [TypeScript Constructor in Interface](http://fritzthecat-blog.blogspot.com/2018/06/typescript-constructor-in-interface.html)
- [TypeScript: useDefineForClassFields – How to avoid future Breaking Changes](https://angular.schule/blog/2022-11-use-define-for-class-fields)

2 changes: 1 addition & 1 deletion docs/comment.md
Original file line number Diff line number Diff line change
@@ -55,8 +55,8 @@ function doStuff(abc: string, xyz: string) {
// do some stuff
}

// @ts-expect-error
expect(() => {
// @ts-expect-error
doStuff(123, 456);
}).toThrow();
```
27 changes: 21 additions & 6 deletions docs/declare.md
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ declare function sayHello(
sayHello('张三');
```

上面示例中,declare 命令给出了`sayHello()`的类型描述,因此可以直接使用它
上面示例中,declare 命令给出了`sayHello()`的类型描述,表示这个函数是由外部文件定义的,因此这里可以直接使用该函数

注意,这种单独的函数类型声明语句,只能用于`declare`命令后面。一方面,TypeScript 不支持单独的函数类型声明语句;另一方面,declare 关键字后面也不能带有函数的具体实现。

@@ -91,7 +91,10 @@ sayHello('张三');
function sayHello(
name:string
):void;
function sayHello(name) {

let foo = 'bar';

function sayHello(name:string) {
return '你好,' + name;
}
```
@@ -206,15 +209,15 @@ declare 关键字的另一个用途,是为外部模块添加属性和方法时
import { Foo as Bar } from 'moduleA';

declare module 'moduleA' {
interface Bar extends Foo {
interface Foo {
custom: {
prop1: string;
}
}
}
```

上面示例中,从模块`moduleA`导入了`Foo`接口,将其重命名为`Bar`并用 declare 关键字为`Bar`增加一个属性`custom`
上面示例中,从模块`moduleA`导入了类型`Foo`,它是一个接口(interface),并将其重命名为`Bar`然后用 declare 关键字为`Foo`增加一个属性`custom`。这里需要注意的是,虽然接口`Foo`改名为`Bar`,但是扩充类型时,还是扩充原始的接口`Foo`,因为同名 interface 会自动合并类型声明

下面是另一个例子。一个项目有多个模块,可以在一个模块中,对另一个模块的接口进行类型扩展。

@@ -249,6 +252,7 @@ const a:A = { x: 0, y: 0 };
某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。

```typescript
// 语法
declare module "模块名";

// 例子
@@ -296,13 +300,13 @@ String.prototype.toSmallString = ():string => {

这个示例第一行的空导出语句`export {}`,作用是强制编译器将这个脚本当作模块处理。这是因为`declare global`必须用在模块里面。

下面的示例是为 window 对象添加一个属性`myAppConfig`
下面的示例是为 window 对象(类型接口为`Window`)添加一个属性`myAppConfig`

```typescript
export {};

declare global {
interface window {
interface Window {
myAppConfig:object;
}
}
@@ -368,6 +372,17 @@ declare module "path" {

上面示例中,`url``path`都是单独的模块脚本,但是它们的类型都定义在`node.d.ts`这个文件里面。

另一种情况是,使用`declare module`命令,为模块名指定加载路径。

```typescript
declare module "lodash" {
export * from "../../dependencies/lodash";
export default from "../../dependencies/lodash";
}
```

上面示例中,`declare module "lodash"`为模块`lodash`,指定具体的加载路径。

使用时,自己的脚本使用三斜杠命令,加载这个类型声明文件。

```typescript
Loading