From 2fdbc00d73f6b1bcd741d057b3e346007f170e4c Mon Sep 17 00:00:00 2001 From: alex12306 <zxc5305138@126.com> Date: Wed, 30 Aug 2023 14:43:03 +0800 Subject: [PATCH 01/46] =?UTF-8?q?docs:=20=E8=AE=A2=E6=AD=A3=EF=BC=9A?= =?UTF-8?q?=E7=AC=94=E8=AF=AF=E3=80=81=E5=85=A8=E8=A7=92=E5=88=86=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/mapping.md | 4 ++-- docs/utility.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/mapping.md b/docs/mapping.md index 603ca9c..ae1c0d7 100644 --- a/docs/mapping.md +++ b/docs/mapping.md @@ -113,7 +113,7 @@ type MyObj = { 上面示例中,`[p in string]`就是属性名索引形式`[p: string]`的映射写法。 -通过映射,可以某个对象的所有属性改成可选属性。 +通过映射,可以把某个对象的所有属性改成可选属性。 ```typescript type A = { @@ -262,7 +262,7 @@ type B = { type B = { fooID: number; barID: number; -}; +}; ``` 上面示例中,类型`B`是类型`A`的映射,但在映射时把属性名改掉了,在原始属性名后面加上了字符串`ID`。 diff --git a/docs/utility.md b/docs/utility.md index 62bdd64..46c64a0 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -660,7 +660,7 @@ type ThisParameterType<T> = T extends ( this: infer U, ...args: never - ) => any ? U : unknown; + ) => any ? U : unknown; ``` ## `ThisType<Type>` From f78c771b24739092b08cc6c30887252064a01a58 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 31 Aug 2023 16:47:25 +0800 Subject: [PATCH 02/46] docs(module): fixed #76 --- docs/module.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/module.md b/docs/module.md index ea6cc32..622f223 100644 --- a/docs/module.md +++ b/docs/module.md @@ -56,14 +56,6 @@ $ tsc b.ts 上面命令发现`b.ts`依赖`a.ts`,就会自动寻找`a.ts`,也将其同时编译,因此编译产物还是`a.js`和`b.js`两个文件。 -如果想将`a.ts`和`b.ts`编译成一个文件,可以使用`--outFile`参数。 - -```typescript -$ tsc --outFile result.js b.ts -``` - -上面示例将`a.ts`和`b.ts`合并编译为`result.js`。 - ## import type 语句 import 在一条语句中,可以同时输入类型和正常接口。 From 2b97ba6959d2ede2aa239a9cdc0f78410781043c Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Fri, 1 Sep 2023 22:08:25 +0800 Subject: [PATCH 03/46] chore: modify chapter title --- chapters.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters.yml b/chapters.yml index 14a2ffe..b1c1825 100644 --- a/chapters.yml +++ b/chapters.yml @@ -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: 注释指令 From 663f3bfa049c45755427a4c62115544adedb528e Mon Sep 17 00:00:00 2001 From: thinkasany <480968828@qq.com> Date: Sat, 2 Sep 2023 15:37:46 +0800 Subject: [PATCH 04/46] docs: perfect description --- docs/any.md | 2 +- docs/function.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/any.md b/docs/any.md index 6026996..067e6e1 100644 --- a/docs/any.md +++ b/docs/any.md @@ -240,7 +240,7 @@ let v2:string = f(); // 不报错 let v3:boolean = f(); // 不报错 ``` -上面示例中,函数`f()`会抛错,所以返回值类型可以写成`never`,即不可能返回任何值。各种其他类型的变量都可以赋值为`f()`的运行结果(`never`类型)。 +上面示例中,函数`f()`会抛出错误,所以返回值类型可以写成`never`,即不可能返回任何值。各种其他类型的变量都可以赋值为`f()`的运行结果(`never`类型)。 为什么`never`类型可以赋值给任意其他类型呢?这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了`never`类型。因此,`never`类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。 diff --git a/docs/function.md b/docs/function.md index 1d95ad5..f92e4c3 100644 --- a/docs/function.md +++ b/docs/function.md @@ -601,7 +601,7 @@ const f3 = function ():void { 上面示例中,函数字面量声明了返回`void`类型,这时只要有返回值(除了`undefined`和`null`)就会报错。 -函数的运行结果如果是抛错,也允许将返回值写成`void`。 +函数的运行结果如果是抛出错误,也允许将返回值写成`void`。 ```typescript function throwErr():void { @@ -609,7 +609,7 @@ function throwErr():void { } ``` -上面示例中,函数`throwErr()`会抛错,返回值类型写成`void`是允许的。 +上面示例中,函数`throwErr()`会抛出错误,返回值类型写成`void`是允许的。 除了函数,其他变量声明为`void`类型没有多大意义,因为这时只能赋值为`undefined`或者`null`(假定没有打开`strictNullChecks`) 。 @@ -634,7 +634,7 @@ function fail(msg:string):never { } ``` -上面示例中,函数`fail()`会抛错,不会正常退出,所以返回值类型是`never`。 +上面示例中,函数`fail()`会抛出错误,不会正常退出,所以返回值类型是`never`。 注意,只有抛出错误,才是 never 类型。如果显式用`return`语句返回一个 Error 对象,返回值就不是 never 类型。 @@ -646,7 +646,7 @@ function fail():Error { 上面示例中,函数`fail()`返回一个 Error 对象,所以返回值类型是 Error。 -另外,由于抛错的情况属于`never`类型或`void`类型,所以无法从返回值类型中获知,抛出的是哪一种错误。 +另外,由于抛出错误的情况属于`never`类型或`void`类型,所以无法从返回值类型中获知,抛出的是哪一种错误。 (2)无限执行的函数。 @@ -696,7 +696,7 @@ function f( 上面示例中,函数`f()`的参数`x`的类型为`string|undefined`。但是,`x`类型为`undefined`时,调用了`neverReturns()`。这个函数不会返回,因此 TypeScript 可以推断出,判断语句后面的那个`x`,类型一定是`string`。 -一个函数如果某些条件下有正常返回值,另一些条件下抛错,这时它的返回值类型可以省略`never`。 +一个函数如果某些条件下有正常返回值,另一些条件下抛出错误,这时它的返回值类型可以省略`never`。 ```typescript function sometimesThrow():number { From 65e5cbac3356f6b2e9f4cd2ba4dedc8bd9605774 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 2 Sep 2023 19:42:49 +0800 Subject: [PATCH 05/46] docs(tsconfig): moduleResolution add bundler --- docs/module.md | 26 +++++++++++++++++--------- docs/tsconfig.json.md | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/module.md b/docs/module.md index 622f223..fdbce43 100644 --- a/docs/module.md +++ b/docs/module.md @@ -223,17 +223,21 @@ console.log(obj.foo); // 123 ## 模块定位 -模块定位(module resolution)指的是确定 import 语句和 export 语句里面的模块文件位置。 +模块定位(module resolution)指的是一种算法,用来确定 import 语句和 export 语句里面的模块文件位置。 ```typescript +// 相对模块 import { TypeA } from './a'; + +// 非相对模块 +import * as $ from "jquery"; ``` -上面示例中,TypeScript 怎么确定`./a`到底是指哪一个模块,这就叫做“模块定位”。 +上面示例中,TypeScript 怎么确定`./a`或`jquery`到底是指哪一个模块,具体位置在哪里,用到的算法就叫做“模块定位”。 -模块定位有两种方法,一种称为 Classic 方法,另一种称为 Node 方法。可以使用编译参数`moduleResolution`,指定使用哪一种方法。 +编译参数`moduleResolution`,用来指定具体使用哪一种定位算法。常用的算法有两种:`Classic`和`Node`。 -没有指定定位方法时,就看原始脚本采用什么模块格式。如果模块格式是 CommonJS(即编译时指定`--module commonjs`),那么模块定位采用 Node 方法,否则采用 Classic 方法(模块格式为 es2015、 esnext、amd, system, umd 等等)。 +如果没有指定`moduleResolution`,它的默认值与编译参数`module`有关。`module`设为`commonjs`时(项目脚本采用 CommonJS 模块格式),`moduleResolution`的默认值为`Node`,即采用 Node.js 的模块定位算法。其他情况下(`module`设为 es2015、 esnext、amd, system, umd 等等),就采用`Classic`定位算法。 ### 相对模块,非相对模块 @@ -245,26 +249,30 @@ import { TypeA } from './a'; - `import { DefaultHeaders } from "../constants/http";` - `import "/mod";` +相对模块的定位,是根据当前脚本的位置进行计算的,一般用于保存在当前项目目录结构中的模块脚本。 + 非相对模块指的是不带有路径信息的模块。下面 import 语句加载的模块,都是非相对模块。 - `import * as $ from "jquery";` - `import { Component } from "@angular/core";` +非相对模块的定位,是由`baseUrl`属性或模块映射而确定的,通常用于加载外部模块。 + ### Classic 方法 Classic 方法以当前脚本的路径作为“基准路径”,计算相对模块的位置。比如,脚本`a.ts`里面有一行代码`import { b } from "./b"`,那么 TypeScript 就会在`a.ts`所在的目录,查找`b.ts`和`b.d.ts`。 -至于非相对模块,也是以当前脚本的路径作为起点,一层层查找上级目录。比如,脚本`a.ts`里面有一行代码`import { b } from "b"`,那么就会查找`b.ts`和`b.d.ts`。 +至于非相对模块,也是以当前脚本的路径作为起点,一层层查找上级目录。比如,脚本`a.ts`里面有一行代码`import { b } from "b"`,那么就会依次在每一级上层目录里面,查找`b.ts`和`b.d.ts`。 ### Node 方法 -Node 方法就是模拟 Node.js 的模块加载方法。 +Node 方法就是模拟 Node.js 的模块加载方法,也就是`require()`的实现方法。 相对模块依然是以当前脚本的路径作为“基准路径”。比如,脚本文件`a.ts`里面有一行代码`let x = require("./b");`,TypeScript 按照以下顺序查找。 -1. 当前目录是否包含`b.ts`、`b.tsx`、`b.d.ts`。 -1. 当前目录是否有子目录`b`,该子目录是否存在文件`package.json`,该文件的`types`字段是否指定了入口文件,如果是的就加载该文件。 -1. 当前目录的子目录`b`是否包含`index.ts`、`index.tsx`、`index.d.ts`。 +1. 当前目录是否包含`b.ts`、`b.tsx`、`b.d.ts`。如果不存在就执行下一步。 +1. 当前目录是否存在子目录`b`,该子目录里面的`package.json`文件是否有`types`字段指定了模块入口文件。如果不存在就执行下一步。 +1. 当前目录的子目录`b`是否包含`index.ts`、`index.tsx`、`index.d.ts`。如果不存在就报错。 非相对模块则是以当前脚本的路径作为起点,逐级向上层目录查找是否存在子目录`node_modules`。比如,脚本文件`a.js`有一行`let x = require("b");`,TypeScript 按照以下顺序进行查找。 diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index 7a86b4b..76de0f4 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -449,8 +449,9 @@ TypeScript 内置的类型描述文件,主要有以下一些,完整的清单 `moduleResolution`确定模块路径的算法,即如何查找模块。它可以取以下四种值。 - `node`:采用 Node.js 的 CommonJS 模块算法。 -- `node16`或`nodenext`:采用 Node.js 的 ECMAScript 模块算法,从 TypeScript 4.7 开始支持。 +- `node16`或`nodenext`:采用 Node.js 的 ECMAScript 模块算法,从 TypeScript 4.7 开始支持。 - `classic`:TypeScript 1.6 之前的算法,新项目不建议使用。 +- `bundle`:TypeScript 5.0 新增的选项,表示当前代码会被其他打包器(比如 Webpack、Vite、esbuild、Parcel、rollup、swc)处理,从而放宽加载规则,它要求`module`设为`es2015`或更高版本,详见加入该功能的 [PR 说明](https://github.com/microsoft/TypeScript/pull/51669)。 它的默认值与`module`属性有关,如果`module`为`AMD`、`UMD`、`System`或`ES6/ES2015`,默认值为`classic`;如果`module`为`node16`或`nodenext`,默认值为这两个值;其他情况下,默认值为`Node`。 From 87034e4fcf34968e9111c3b3b60bee8625226a15 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 2 Sep 2023 19:44:28 +0800 Subject: [PATCH 06/46] docs(tsconfig): moduleResolution add bundler --- docs/tsconfig.json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index 76de0f4..7323e7d 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -451,7 +451,7 @@ TypeScript 内置的类型描述文件,主要有以下一些,完整的清单 - `node`:采用 Node.js 的 CommonJS 模块算法。 - `node16`或`nodenext`:采用 Node.js 的 ECMAScript 模块算法,从 TypeScript 4.7 开始支持。 - `classic`:TypeScript 1.6 之前的算法,新项目不建议使用。 -- `bundle`:TypeScript 5.0 新增的选项,表示当前代码会被其他打包器(比如 Webpack、Vite、esbuild、Parcel、rollup、swc)处理,从而放宽加载规则,它要求`module`设为`es2015`或更高版本,详见加入该功能的 [PR 说明](https://github.com/microsoft/TypeScript/pull/51669)。 +- `bundler`:TypeScript 5.0 新增的选项,表示当前代码会被其他打包器(比如 Webpack、Vite、esbuild、Parcel、rollup、swc)处理,从而放宽加载规则,它要求`module`设为`es2015`或更高版本,详见加入该功能的 [PR 说明](https://github.com/microsoft/TypeScript/pull/51669)。 它的默认值与`module`属性有关,如果`module`为`AMD`、`UMD`、`System`或`ES6/ES2015`,默认值为`classic`;如果`module`为`node16`或`nodenext`,默认值为这两个值;其他情况下,默认值为`Node`。 From 5732aca414709ce908ca4448277621ddcbf9d94a Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 9 Sep 2023 12:32:58 +0800 Subject: [PATCH 07/46] docs(function): edit function overload --- docs/function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/function.md b/docs/function.md index f92e4c3..fd46274 100644 --- a/docs/function.md +++ b/docs/function.md @@ -889,7 +889,7 @@ type CreateElement = { } ``` -由于重载是一种比较复杂的类型声明方法,为了降低复杂性,一般来说,如果可以的话,应该优先使用联合类型替代函数重载。 +由于重载是一种比较复杂的类型声明方法,为了降低复杂性,一般来说,如果可以的话,应该优先使用联合类型替代函数重载,除非多个参数之间、或者某个参数与返回值之间,存在对应关系。 ```typescript // 写法一 From 4583162ae56d0a2bc6ad08c9af1352d20b0ebafd Mon Sep 17 00:00:00 2001 From: biubiubiu999 <houshengaaa@gmail.com> Date: Sun, 10 Sep 2023 00:13:09 +0800 Subject: [PATCH 08/46] Update operator.md fix typo --- docs/operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator.md b/docs/operator.md index 56fffdf..e892636 100644 --- a/docs/operator.md +++ b/docs/operator.md @@ -466,7 +466,7 @@ type Flatten<Type, Item> = Type extends Array<Item> ? Item : Type; ``` -上面是不使用`infer`的写法,每次调用`Fleatten`的时候,都要传入两个参数,就比较麻烦。 +上面是不使用`infer`的写法,每次调用`Flatten`的时候,都要传入两个参数,就比较麻烦。 下面的例子使用`infer`,推断函数的参数类型和返回值类型。 From 5d83ec0e8b59d929d7dbf50bd868e682cd3966d4 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sun, 10 Sep 2023 16:32:45 +0800 Subject: [PATCH 09/46] docs(tsconfig.json): edit typeRoots & types --- docs/tsconfig.json.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index 7323e7d..daaed15 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -791,7 +791,9 @@ class User { ### typeRoots -`typeRoots`设置类型模块所在的目录,默认是`node_modules/@types`。 +`typeRoots`设置类型模块所在的目录,默认是`node_modules/@types`,该目录里面的模块会自动加入编译。一旦指定了该属性,就不会再用默认值`node_modules/@types`里面的类型模块。 + +该属性的值是一个数组,数组的每个成员就是一个目录,它们的路径是相对于`tsconfig.json`位置。 ```typescript { @@ -803,7 +805,7 @@ class User { ### types -`types`设置`typeRoots`目录下需要包括在编译之中的类型模块。默认情况下,该目录下的所有类型模块,都会自动包括在编译之中。 +默认情况下,`typeRoots`目录下所有模块都会自动加入编译,如果指定了`types`属性,那么只有其中列出的模块才会自动加入编译。 ```typescript { @@ -813,6 +815,10 @@ class User { } ``` +上面的设置表示,默认情况下,只有`./node_modules/@types/node`、`./node_modules/@types/jest`和`./node_modules/@types/express`会自动加入编译,其他`node_modules/@types/`目录下的模块不会加入编译。 + +如果`"types": []`,就表示不会自动将所有`@types`模块加入编译。 + ### useUnknownInCatchVariables `useUnknownInCatchVariables`设置`catch`语句捕获的`try`抛出的返回值类型,从`any`变成`unknown`。 From b567a611d4d66ded89f886f2cb76f1d4758add43 Mon Sep 17 00:00:00 2001 From: Cui Yang <cuiyang673308817@gmail.com> Date: Fri, 15 Sep 2023 12:09:07 +0800 Subject: [PATCH 10/46] docs(function): fix typo --- docs/function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/function.md b/docs/function.md index fd46274..1224f1c 100644 --- a/docs/function.md +++ b/docs/function.md @@ -571,7 +571,7 @@ const ret = []; src.forEach(el => ret.push(el)); ``` -上面示例中,`push()`有返回值,表示新插入的元素在数组里面的位置。但是,对于`forEach()`方法来说,这个返回值是没有作用的,根本用不到,所以 TypeScript 不会报错。 +上面示例中,`push()`有返回值,表示插入新元素后数组的长度。但是,对于`forEach()`方法来说,这个返回值是没有作用的,根本用不到,所以 TypeScript 不会报错。 如果后面使用了这个函数的返回值,就违反了约定,则会报错。 From 0e8f8a92fab6fe5955e3842ed2e6f580c7b0e6d4 Mon Sep 17 00:00:00 2001 From: weishen-hong <57124685+weihenHong@users.noreply.github.com> Date: Fri, 15 Sep 2023 18:04:48 +0800 Subject: [PATCH 11/46] fix typo --- docs/interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interface.md b/docs/interface.md index b6dd5bc..a3b3a3f 100644 --- a/docs/interface.md +++ b/docs/interface.md @@ -526,7 +526,7 @@ type Country = { capital: string; } -interface Coutry { +interface Country { name: string; capital: string; } From 8ad5fc69f7e19dd117ddfc60b3dee299afca0180 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Wed, 22 Nov 2023 00:07:23 +0800 Subject: [PATCH 12/46] docs(decorator): fixed #84 --- docs/decorator.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/decorator.md b/docs/decorator.md index c8d0805..8aea41c 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -818,6 +818,9 @@ class T { @d('实例属性') instanceField = log('实例属性值'); + + @d('静态方法装饰器') + static fn(){} } ``` @@ -826,16 +829,18 @@ class T { 它的运行结果如下。 ```typescript -// "评估 @d(): 类装饰器" -// "评估 @d(): 静态属性装饰器" -// "评估 @d(): 原型方法" -// "计算方法名" -// "评估 @d(): 实例属性" -// "应用 @d(): 原型方法" -// "应用 @d(): 静态属性装饰器" -// "应用 @d(): 实例属性" -// "应用 @d(): 类装饰器" -// "静态属性值" +评估 @d(): 类装饰器 +评估 @d(): 静态属性装饰器 +评估 @d(): 原型方法 +计算方法名 +评估 @d(): 实例属性 +评估 @d(): 静态方法装饰器 +应用 @d(): 静态方法装饰器 +应用 @d(): 原型方法 +应用 @d(): 静态属性装饰器 +应用 @d(): 实例属性 +应用 @d(): 类装饰器 +静态属性值 ``` 可以看到,类载入的时候,代码按照以下顺序执行。 @@ -846,7 +851,7 @@ class T { (2)装饰器应用:实际执行装饰器函数,将它们与对应的方法和属性进行结合。 -原型方法的装饰器首先应用,然后是静态属性和静态方法装饰器,接下来是实例属性装饰器,最后是类装饰器。 +静态方法装饰器首先应用,然后是原型方法的装饰器和静态属性装饰器,接下来是实例属性装饰器,最后是类装饰器。 注意,“实例属性值”在类初始化的阶段并不执行,直到类实例化时才会执行。 From 68962e8a1adfcb6573c51509c4ab1d86bf1eefab Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 23 Nov 2023 20:53:20 +0800 Subject: [PATCH 13/46] docs(enum): fix #85 --- docs/enum.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/enum.md b/docs/enum.md index fed20c8..f9656b3 100644 --- a/docs/enum.md +++ b/docs/enum.md @@ -512,15 +512,14 @@ type Foo = keyof typeof MyEnum; 上面示例中,`keyof typeof MyEnum`可以取出`MyEnum`的所有成员名,所以类型`Foo`等同于联合类型`'A'|'B'`。 -注意,这里的`typeof`是必需的,否则`keyof MyEnum`相当于`keyof number`。 +注意,这里的`typeof`是必需的,否则`keyof MyEnum`相当于`keyof string`。 ```typescript type Foo = keyof MyEnum; -// "toString" | "toFixed" | "toExponential" | -// "toPrecision" | "valueOf" | "toLocaleString" +// number | typeof Symbol.iterator | "toString" | "charAt" | "charCodeAt" | ... ``` -上面示例中,类型`Foo`等于类型`number`的所有原生属性名组成的联合类型。 +上面示例中,类型`Foo`等于类型`string`的所有原生属性名组成的联合类型。这是`MyEnum`为字符串 Enum 的结果,如果`MyEnum`是数值 Enum,那么`keyof MyEnum`相当于`keyof number`。 这是因为 Enum 作为类型,本质上属于`number`或`string`的一种变体,而`typeof MyEnum`会将`MyEnum`当作一个值处理,从而先其转为对象类型,就可以再用`keyof`运算符返回该对象的所有属性名。 From a30b9442fc024a77c1a3a60478deb42194d5e8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E6=98=BE?= <lixian@yuanian.com> Date: Wed, 29 Nov 2023 16:39:41 +0800 Subject: [PATCH 14/46] fix: delete Superfluous words --- docs/tsconfig.json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index daaed15..ce618c7 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -579,7 +579,7 @@ TypeScript 内置的类型描述文件,主要有以下一些,完整的清单 ### rootDirs -`rootDirs`把多个不同目录,合并成一个目虚拟目录,便于模块定位。 +`rootDirs`把多个不同目录,合并成一个虚拟目录,便于模块定位。 ```typescript { From abad608483dc32f7983b5141acbc57ae6eef5774 Mon Sep 17 00:00:00 2001 From: hermia <HQQhouqinqin0612@outlook.com> Date: Thu, 30 Nov 2023 15:21:29 +0800 Subject: [PATCH 15/46] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E4=B8=AD=E5=B1=9E=E6=80=A7=E5=90=8D=E7=9A=84?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E7=B1=BB=E5=9E=8B=E4=B8=AD=E7=9A=84=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E9=94=99=E8=AF=AF=EF=BC=8C=E5=B0=86=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=E5=B1=9E=E6=80=A7=E5=90=8D=E7=AC=A6=20?= =?UTF-8?q?=E5=90=88=E5=B1=9E=E6=80=A7=E5=90=8D=E7=B4=A2=E5=BC=95=E7=9A=84?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=EF=BC=8C=E4=B8=A4=E8=80=85=E4=B8=8D=E8=83=BD?= =?UTF-8?q?=E6=9C=89=E5=86=B2=E7=AA=81=EF=BC=8C=E5=90=A6=E5=88=99=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E3=80=82=E4=BF=AE=E6=94=B9=E6=88=90=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E5=8D=95=E4=B8=AA=E5=B1=9E=E6=80=A7=E5=90=8D=E4=B8=8D=E7=AC=A6?= =?UTF-8?q?=E5=90=88=E5=B1=9E=E6=80=A7=E5=90=8D=E7=B4=A2=E5=BC=95=E7=9A=84?= =?UTF-8?q?=E8=8C=83=E5=9B=B4=EF=BC=8C=E4=B8=A4=E8=80=85=E5=8F=91=E7=94=9F?= =?UTF-8?q?=E5=86=B2=E7=AA=81=EF=BC=8C=E5=B0=B1=E4=BC=9A=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/object.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/object.md b/docs/object.md index 8151363..3eb9bcb 100644 --- a/docs/object.md +++ b/docs/object.md @@ -423,7 +423,7 @@ type MyType = { 上面示例中,类型`MyType`同时有两种属性名索引,但是数值索引与字符串索引冲突了,所以报错了。由于字符属性名的值类型是`string`,数值属性名的值类型只有同样为`string`,才不会报错。 -同样地,可以既声明属性名索引,也声明具体的单个属性名。如果单个属性名符合属性名索引的范围,两者不能有冲突,否则报错。 +同样地,可以既声明属性名索引,也声明具体的单个属性名。如果单个属性名不符合属性名索引的范围,两者发生冲突,就会报错。 ```typescript type MyType = { From d8b2606434740847951c9ee3ff5497203e6c575f Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 7 Dec 2023 10:34:26 +0800 Subject: [PATCH 16/46] docs(decorator): fix example --- docs/decorator.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/decorator.md b/docs/decorator.md index 8aea41c..821a988 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -251,10 +251,10 @@ inst1.count // 1 ```typescript function functionCallable( - value as any, {kind} as any -) { + value:any, {kind}:any +):any { if (kind === 'class') { - return function (...args) { + return function (...args:any) { if (new.target !== undefined) { throw new TypeError('This function can’t be new-invoked'); } @@ -265,10 +265,13 @@ function functionCallable( @functionCallable class Person { - constructor(name) { + name:string; + constructor(name:string) { this.name = name; } } + +// @ts-ignore const robin = Person('Robin'); robin.name // 'Robin' ``` From f77f8d1b129acab42ac515e8981130ba9281fcab Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 11 Dec 2023 14:03:19 +0800 Subject: [PATCH 17/46] docs(declare): fixed #91 --- docs/declare.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/declare.md b/docs/declare.md index f1eb9b9..fde7dca 100644 --- a/docs/declare.md +++ b/docs/declare.md @@ -296,13 +296,13 @@ String.prototype.toSmallString = ():string => { 这个示例第一行的空导出语句`export {}`,作用是强制编译器将这个脚本当作模块处理。这是因为`declare global`必须用在模块里面。 -下面的示例是为 window 对象添加一个属性`myAppConfig`。 +下面的示例是为 window 对象(类型接口为`Window`)添加一个属性`myAppConfig`。 ```typescript export {}; declare global { - interface window { + interface Window { myAppConfig:object; } } From eb048a350909811c793a9aee6b3e53faaf3e5fbb Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Wed, 13 Dec 2023 13:53:59 +0800 Subject: [PATCH 18/46] docs: fixed #92 --- docs/tsconfig.json.md | 2 +- docs/utility.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index ce618c7..b31d427 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -156,7 +156,7 @@ $ yarn add --dev @tsconfig/deno } ``` -## compileOptions +## compilerOptions `compilerOptions`属性用来定制编译行为。这个属性可以省略,这时编译器将使用默认设置。 diff --git a/docs/utility.md b/docs/utility.md index 46c64a0..0182f97 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -467,7 +467,7 @@ type Mutable<T> = { 上面代码中,`-readonly`表示去除属性的只读标志。 -相应地,`+readonly`就表示增加只读标志,等同于`readonly`。因此,`ReadOnly<Type>`的实现也可以写成下面这样。 +相应地,`+readonly`就表示增加只读标志,等同于`readonly`。因此,`Readonly<Type>`的实现也可以写成下面这样。 ```typescript type Readonly<T> = { From 838b2f69f4ad91f907feaf20d7c39997713df56b Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Wed, 13 Dec 2023 14:19:00 +0800 Subject: [PATCH 19/46] docs(declare): fixed wrong example --- docs/declare.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/declare.md b/docs/declare.md index fde7dca..ec85094 100644 --- a/docs/declare.md +++ b/docs/declare.md @@ -206,7 +206,7 @@ declare 关键字的另一个用途,是为外部模块添加属性和方法时 import { Foo as Bar } from 'moduleA'; declare module 'moduleA' { - interface Bar extends Foo { + interface Bar { custom: { prop1: string; } From d21f77eebbc767596b185255b0459cd0a3917ed7 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 16 Dec 2023 11:14:14 +0800 Subject: [PATCH 20/46] docs(declare): fixed error --- docs/declare.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/declare.md b/docs/declare.md index ec85094..960c20d 100644 --- a/docs/declare.md +++ b/docs/declare.md @@ -206,7 +206,7 @@ declare 关键字的另一个用途,是为外部模块添加属性和方法时 import { Foo as Bar } from 'moduleA'; declare module 'moduleA' { - interface Bar { + interface Foo { custom: { prop1: string; } @@ -214,7 +214,7 @@ declare module 'moduleA' { } ``` -上面示例中,从模块`moduleA`导入了`Foo`接口,将其重命名为`Bar`,并用 declare 关键字为`Bar`增加一个属性`custom`。 +上面示例中,从模块`moduleA`导入了类型`Foo`,它是一个接口(interface),并将其重命名为`Bar`,然后用 declare 关键字为`Foo`增加一个属性`custom`。这里需要注意的是,虽然接口`Foo`改名为`Bar`,但是扩充类型时,还是扩充原始的接口`Foo`,因为同名 interface 会自动合并类型声明。 下面是另一个例子。一个项目有多个模块,可以在一个模块中,对另一个模块的接口进行类型扩展。 From ef7d6197955c55eaa2c364114eaf04d3074b08eb Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 16 Dec 2023 14:51:21 +0800 Subject: [PATCH 21/46] docs: fixed typos --- docs/comment.md | 2 +- docs/tsconfig.json.md | 2 +- docs/utility.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/comment.md b/docs/comment.md index 38ddfa7..126cbe7 100644 --- a/docs/comment.md +++ b/docs/comment.md @@ -55,8 +55,8 @@ function doStuff(abc: string, xyz: string) { // do some stuff } -// @ts-expect-error expect(() => { + // @ts-expect-error doStuff(123, 456); }).toThrow(); ``` diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index b31d427..93f7add 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -629,7 +629,7 @@ TypeScript 内置的类型描述文件,主要有以下一些,完整的清单 - strictPropertyInitialization - noImplicitAny - noImplicitThis -- useUnknownInCatchVaria +- useUnknownInCatchVariables 打开`strict`的时候,允许单独关闭其中一项。 diff --git a/docs/utility.md b/docs/utility.md index 0182f97..d177337 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -644,7 +644,7 @@ type ReturnType< `ThisParameterType<Type>`提取函数类型中`this`参数的类型。 ```typescript -function toHex(this: Number) { +function toHex(this:number) { return this.toString(16); } From 68da0fb373aa5b82900d76340e5f1fcd0144458b Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 21 Dec 2023 22:48:08 +0800 Subject: [PATCH 22/46] docs(decorator): fixed #93 --- docs/decorator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decorator.md b/docs/decorator.md index 821a988..8696b0e 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -691,7 +691,7 @@ class C { } ``` -上面示例中,`accessor`修饰符等同于为属性`x`自动生成取值器和存值器,它们作用于私有属性`x`。也就是说,上面的代码等同于下面的代码。 +上面示例中,`accessor`修饰符等同于为公开属性`x`自动生成取值器和存值器,它们作用于私有属性`x`。(注意,公开的`x`与私有的`x`不是同一个属性。)也就是说,上面的代码等同于下面的代码。 ```typescript class C { From 0299aa9604f775b673dfc9d69251ed2027a181bb Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Tue, 26 Dec 2023 22:37:03 +0800 Subject: [PATCH 23/46] docs(interface): fixed typo --- docs/interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/interface.md b/docs/interface.md index a3b3a3f..e0169f8 100644 --- a/docs/interface.md +++ b/docs/interface.md @@ -394,7 +394,7 @@ interface Box { 这样的设计主要是为了兼容 JavaScript 的行为。JavaScript 开发者常常对全局对象或者外部库,添加自己的属性和方法。那么,只要使用 interface 给出这些自定义属性和方法的类型,就能自动跟原始的 interface 合并,使得扩展外部类型非常方便。 -举例来说,Web 网页开发经常会对`windows`对象和`document`对象添加自定义属性,但是 TypeScript 会报错,因为原始定义没有这些属性。解决方法就是把自定义属性写成 interface,合并进原始定义。 +举例来说,Web 网页开发经常会对`window`对象和`document`对象添加自定义属性,但是 TypeScript 会报错,因为原始定义没有这些属性。解决方法就是把自定义属性写成 interface,合并进原始定义。 ```typescript interface Document { From 84474db9268b8c8f721a64f1a0584c80a914b121 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Tue, 9 Jan 2024 10:45:30 +0800 Subject: [PATCH 24/46] docs(module): fixed #96 --- docs/module.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/module.md b/docs/module.md index fdbce43..f481bc3 100644 --- a/docs/module.md +++ b/docs/module.md @@ -84,17 +84,19 @@ import { type A, a } from './a'; 上面示例中,import 语句输入的类型`A`前面有`type`关键字,表示这是一个类型。 -第二个方法是使用 import type 语句,这个语句只能输入类型,不能输入正常接口。 +第二个方法是使用 import type 语句,这个语句只用来输入类型,不用来输入正常接口。 ```typescript // 正确 import type { A } from './a'; +let b:A = 'hello'; // 报错 import type { a } from './a'; +let b = a; ``` -上面示例中,import type 输入类型`A`是正确的,但是输入正常接口`a`就会报错。 +上面示例中,import type 输入类型`A`是正确的,可以把`A`当作类型使用。但是,输入正常接口`a`,并把`a`当作一个值使用,就会报错。这就是说,看到`import type`,你就知道它输入的肯定是类型。 import type 语句也可以输入默认类型。 From 4c7a0cbbebbb22ff84a9606934954fe552d6ac2b Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 18 Mar 2024 18:05:05 +0800 Subject: [PATCH 25/46] docs(assert): fixed #100 --- docs/assert.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/assert.md b/docs/assert.md index 5c6659c..64aad74 100644 --- a/docs/assert.md +++ b/docs/assert.md @@ -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`。 - ## 类型断言的条件 类型断言并不意味着,可以把某个值断言为任意类型。 From 5d4a2f1484facd2b4234e83f9e7ed851519e5797 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Tue, 19 Mar 2024 16:28:18 +0800 Subject: [PATCH 26/46] docs(operator): fixed #101 --- docs/operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator.md b/docs/operator.md index e892636..5416439 100644 --- a/docs/operator.md +++ b/docs/operator.md @@ -588,7 +588,7 @@ type Greeting = `hello ${World}`; 上面示例中,类型`Greeting`是一个模板字符串,里面引用了另一个字符串类型`world`,因此`Greeting`实际上是字符串`hello world`。 -注意,模板字符串可以引用的类型一共6种,分别是 string、number、bigint、boolean、null、undefined。引用这6种以外的类型会报错。 +注意,模板字符串可以引用的类型一共7种,分别是 string、number、bigint、boolean、null、undefined、Enum。引用这7种以外的类型会报错。 ```typescript type Num = 123; From d5e0f47a20d1f2769dce610671a08033ac610d8e Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 21 Mar 2024 21:29:42 +0800 Subject: [PATCH 27/46] docs(function): fixed #102 --- docs/function.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/function.md b/docs/function.md index 1224f1c..9badcf1 100644 --- a/docs/function.md +++ b/docs/function.md @@ -498,6 +498,8 @@ function arraySum( 上面示例中,参数`arr`的类型是`readonly number[]`,表示为只读参数。如果函数体内部修改这个数组,就会报错。 +注意,`readonly`关键字目前只允许用在数组和元组类型的参数前面,如果用在其他类型的参数前面,就会报错。 + ## void 类型 void 类型表示函数没有返回值。 From 57cdc82253554a4eaff53cb2fcf8d2ed06b87b34 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Wed, 10 Apr 2024 16:54:05 +0800 Subject: [PATCH 28/46] docs(function): fixed typo --- docs/function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/function.md b/docs/function.md index 9badcf1..6795e59 100644 --- a/docs/function.md +++ b/docs/function.md @@ -374,7 +374,7 @@ function f(x = 456) { return x; } -f2(undefined) // 456 +f(undefined) // 456 ``` 具有默认值的参数如果不位于参数列表的末尾,调用时不能省略,如果要触发默认值,必须显式传入`undefined`。 From ae9bea9a37872b5dcdca75800aa2dd211450d86f Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 23 May 2024 23:16:41 +0800 Subject: [PATCH 29/46] =?UTF-8?q?docs(declare):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E5=8D=95=E7=8B=AC=E5=86=99=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=A3=B0=E6=98=8E=E7=9A=84=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/declare.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/declare.md b/docs/declare.md index 960c20d..ca7665a 100644 --- a/docs/declare.md +++ b/docs/declare.md @@ -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; } ``` From 1605a244987f8e669e36662bb28b73530bd69ffb Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 8 Jun 2024 16:32:28 +0800 Subject: [PATCH 30/46] refactor: update dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 9848fbb..18efa39 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "author": "Ruan Yifeng", "license": "CC-BY-SA-4.0", "dependencies": { - "gh-pages": "^5.0.0", + "gh-pages": "6.x", "loppo": "^0.6.25", - "loppo-theme-wangdoc": "^0.6.6" + "loppo-theme-wangdoc": "^0.7.1" } } From 22904d3e2db8be745d69f0f2f75c4b1c13e19ede Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 8 Jun 2024 16:38:16 +0800 Subject: [PATCH 31/46] refactor: update github action script --- .github/workflows/wangdoc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wangdoc.yml b/.github/workflows/wangdoc.yml index 29b9a5b..8b652ac 100644 --- a/.github/workflows/wangdoc.yml +++ b/.github/workflows/wangdoc.yml @@ -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 From f665b3f262b9d4922c95f30f428568183c6fff75 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 8 Jun 2024 16:40:18 +0800 Subject: [PATCH 32/46] docs(utility): fixed #107 --- docs/utility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utility.md b/docs/utility.md index d177337..af454c2 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -44,7 +44,7 @@ type Awaited<T> = } ? F extends ( value: infer V, ...args: infer _ - ) => any ? Awaited<...> : never: + ) => any ? Awaited<V> : never: T; ``` From 73e9fdc014eaa82a0ecfc05ee5485f64a7d20881 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 10 Jun 2024 07:47:18 +0800 Subject: [PATCH 33/46] docs(utility): edit text --- docs/utility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/utility.md b/docs/utility.md index af454c2..02a03f9 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -2,7 +2,7 @@ TypeScript 提供了一些内置的类型工具,用来方便地处理各种类型,以及生成新的类型。 -TypeScript 内置了17个类型工具,可以直接使用。 +这些类型工具都是语言本身提供的,可以直接使用。 ## `Awaited<Type>` From 066605460981d30e3afea06ceab6b3df41737b09 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 22 Jun 2024 20:12:43 +0800 Subject: [PATCH 34/46] docs(object): fixed #109 --- docs/object.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/object.md b/docs/object.md index 3eb9bcb..3141870 100644 --- a/docs/object.md +++ b/docs/object.md @@ -489,8 +489,7 @@ let { x: foo, y: bar } ```typescript function draw({ shape: Shape, - xPos: number = 100, - yPos: number = 100 + xPos: number = 100 }) { let myShape = shape; // 报错 let x = xPos; // 报错 From a7d9796ed5f544880fc03e2192ecf61f9789fa88 Mon Sep 17 00:00:00 2001 From: LOUSANPANG <36870764+LOUSANPANG@users.noreply.github.com> Date: Thu, 27 Jun 2024 22:08:42 +0800 Subject: [PATCH 35/46] docs(class):correction words "MotoVehicle" -> "MotorVehicle" --- docs/class.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/class.md b/docs/class.md index 7ad78d9..fe3b971 100644 --- a/docs/class.md +++ b/docs/class.md @@ -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`可以调用这些属性和方法。 作为类型使用时,类名只能表示实例的类型,不能表示类的自身类型。 From bbde4688b44bda3e4390d4755e0648cec26a1505 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 1 Jul 2024 18:24:12 +0800 Subject: [PATCH 36/46] docs(class): modify useDefineForClassFields, fixed #113 --- docs/class.md | 193 +++++++++++++++++++++++++++++------------- docs/tsconfig.json.md | 6 ++ 2 files changed, 140 insertions(+), 59 deletions(-) diff --git a/docs/class.md b/docs/class.md index fe3b971..947c510 100644 --- a/docs/class.md +++ b/docs/class.md @@ -954,65 +954,6 @@ class Test extends getGreeterBase() { 上面示例中,例一和例二的`extends`关键字后面都是构造函数,例三的`extends`关键字后面是一个表达式,执行后得到的也是一个构造函数。 -对于那些只设置了类型、没有初值的顶层属性,有一个细节需要注意。 - -```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`的顶层成员`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)。 - -解决方法就是使用`declare`命令,去声明顶层成员的类型,告诉 TypeScript 这些成员的赋值由基类实现。 - -```typescript -class DogHouse extends AnimalHouse { - declare resident: Dog; - - constructor(dog:Dog) { - super(dog); - } -} -``` - -上面示例中,`resident`属性的类型声明前面用了`declare`命令,这样就能确保在编译目标大于等于`ES2022`时(或者打开`useDefineForClassFields`时),代码行为正确。 - ## 可访问性修饰符 类的内部成员的外部可访问性,由三个可访问性修饰符(access modifiers)控制:`public`、`private`和`protected`。 @@ -1278,6 +1219,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`关键字,定义静态成员。 diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index 93f7add..ad4009a 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -819,6 +819,12 @@ class User { 如果`"types": []`,就表示不会自动将所有`@types`模块加入编译。 +### useDefineForClassFields + +`useDefineForClassFields`这个设置针对的是,在类(class)的顶部声明的属性。TypeScript 早先对这一类属性的处理方法,与写入 ES2022 标准的处理方法不一致。这个设置设为`true`,就用来开启 ES2022 的处理方法,设为`false`就是 TypeScript 原有的处理方法。 + +它的默认值跟`target`属性有关,如果编译目标是`ES2022`或更高,那么`useDefineForClassFields`默认值为`true`,否则为`false`。 + ### useUnknownInCatchVariables `useUnknownInCatchVariables`设置`catch`语句捕获的`try`抛出的返回值类型,从`any`变成`unknown`。 From 8f59ef87cf9fd37259a7e041251664718cc2c8fa Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 1 Jul 2024 18:29:04 +0800 Subject: [PATCH 37/46] docs(class): add reference link --- docs/class.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/class.md b/docs/class.md index 947c510..26c094d 100644 --- a/docs/class.md +++ b/docs/class.md @@ -1670,4 +1670,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) From dd06fc53e10ba25a83c24602a1ee438d0d5be58e Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 3 Aug 2024 01:19:54 +0800 Subject: [PATCH 38/46] docs(class): add override keyword #114 --- docs/class.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/class.md b/docs/class.md index 26c094d..0ac8567 100644 --- a/docs/class.md +++ b/docs/class.md @@ -954,6 +954,48 @@ class Test extends getGreeterBase() { 上面示例中,例一和例二的`extends`关键字后面都是构造函数,例三的`extends`关键字后面是一个表达式,执行后得到的也是一个构造函数。 +## override 关键字 + +子类继承父类时,可以覆盖父类的同名方法。 + +```typescript +class A { + show() { + // ... + } + hide() { + // ... + } +} +class B extends A { + show() { + // ... + } + hide() { + // ... + } +} +``` + +上面示例中,B 类定义了自己的`show()`方法和`hide()`方法,覆盖了 A 类的同名方法。 + +但是有些时候,我们继承他人的类,可能会在不知不觉中,就覆盖了他人的方法。为了防止这种情况,TypeScript 4.3 引入了 [override 关键字](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html#override-and-the---noimplicitoverride-flag)。 + +```typescript +class B extends A { + override show() { + // ... + } + override hide() { + // ... + } +} +``` + +上面示例中,B 类的`show()`方法和`hide()`方法前面加了 override 关键字,明确表明作者的意图,就是要覆盖 A 类里面的这两个同名方法。这时,如果 A 类没有定义自己的`show()`方法和`hide()`方法,就会报错。 + +但是,这依然没有解决,子类无意中覆盖父类同名方法的问题。因此,TypeScript 又提供了一个编译参数`noImplicitOverride`。一旦打开这个参数,子类覆盖父类的同名方法就会报错,除非使用了 override 关键字。 + ## 可访问性修饰符 类的内部成员的外部可访问性,由三个可访问性修饰符(access modifiers)控制:`public`、`private`和`protected`。 From b376397025c99b596a20b7d318f9ac8eed36e348 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 23 Sep 2024 11:08:00 +0800 Subject: [PATCH 39/46] docs(assert): fixed #119 --- docs/assert.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/assert.md b/docs/assert.md index 64aad74..7026698 100644 --- a/docs/assert.md +++ b/docs/assert.md @@ -490,18 +490,9 @@ function assertIsDefined<T>( 上面示例中,工具类型`NonNullable<T>`对应类型`T`去除空类型后的剩余类型。 -如果要将断言函数用于函数表达式,可以采用下面的写法。 +如果要将断言函数用于函数表达式,可以采用下面的写法。根据 TypeScript 的[要求](https://github.com/microsoft/TypeScript/pull/33622),这时函数表达式所赋予的变量,必须有明确的类型声明。 ```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; From f68cdbc9108f332ab1b78f4d1ac95059e9945edb Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Mon, 23 Sep 2024 11:12:42 +0800 Subject: [PATCH 40/46] docs(assert): edit reference link --- docs/assert.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assert.md b/docs/assert.md index 7026698..f816bab 100644 --- a/docs/assert.md +++ b/docs/assert.md @@ -490,7 +490,7 @@ function assertIsDefined<T>( 上面示例中,工具类型`NonNullable<T>`对应类型`T`去除空类型后的剩余类型。 -如果要将断言函数用于函数表达式,可以采用下面的写法。根据 TypeScript 的[要求](https://github.com/microsoft/TypeScript/pull/33622),这时函数表达式所赋予的变量,必须有明确的类型声明。 +如果要将断言函数用于函数表达式,可以采用下面的写法。根据 TypeScript 的[要求](https://github.com/microsoft/TypeScript/pull/33622#issuecomment-575301357),这时函数表达式所赋予的变量,必须有明确的类型声明。 ```typescript type AssertIsNumber = From 5c56fd550e31dddfa3a735acdc9569a63d454e53 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sun, 29 Sep 2024 19:29:11 +0800 Subject: [PATCH 41/46] docs(enum): fixed #122 --- docs/enum.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/enum.md b/docs/enum.md index f9656b3..9b4127d 100644 --- a/docs/enum.md +++ b/docs/enum.md @@ -312,7 +312,7 @@ enum Foo { // 等同于 enum Foo { A, - B = 1, + B = 1, C = 2 } ``` diff --git a/package.json b/package.json index 18efa39..f7f09b8 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,6 @@ "dependencies": { "gh-pages": "6.x", "loppo": "^0.6.25", - "loppo-theme-wangdoc": "^0.7.1" + "loppo-theme-wangdoc": "^0.7.3" } } From e7e1a775c8ea9610876ae9e736d1731aec220e83 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 3 Oct 2024 22:16:29 +0800 Subject: [PATCH 42/46] docs(tsconfig.json): edit strictNullChecks --- docs/tsconfig.json.md | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index ad4009a..a6b82db 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -677,28 +677,40 @@ let func:StringOrNumberFunc = fn; ### strictNullChecks -`strictNullChecks`设置对`null`和`undefined`进行严格类型检查。如果打开`strict`属性,这一项就会自动设为`true`,否则为`false`。 +不打开`strictNullChecks`的情况下,变量可以设为`undefined`或`null`,而不管其类型是什么。 -```bash -let value:string; +```typescript +// 不打开 strictNullChecks 的情况 +let x:number; -// strictNullChecks:false -// 下面语句不报错 -value = null; +x = undefined; // 不报错 +x = null; // 不报错 ``` -它可以理解成只要打开,就需要显式检查`null`或`undefined`。 +上面示例中,变量`x`的类型是`number`,但是赋值为`undefined`或`null`都不会报错。这是为了继承 JavaScript 的设定:当变量没有赋值时,它的值就为`undefined`。 + +一旦打开`strictNullChecks`,就相当于从变量的值里面,排除了`undefined`和`null`,除非变量的类型是这两种类型。 ```typescript -function doSomething(x:string|null) { - if (x === null) { - // do nothing - } else { - console.log("Hello, " + x.toUpperCase()); - } -} +// 打开 strictNullChecks 的情况 +let x:number; + +x = undefined; // 报错 +x = null; // 报错 ``` +下面是一个例子。 + +```typescript +// 打开 strickNullChecks 时,类型 A 为 number +// 不打开时,类型 A 为 string +type A = unknown extends {} ? string : number; +``` + +上面示例中,`{}`代表了 Object 类型,不打开`strictNullChecks`时,它包括了`undefined`和`null`,就相当于包括了所有类型的值,所以这时`unknown`类型可以赋值给`{}`类型,类型`A`就为`number`。打开`strictNullChecks`时,`{}`就排除掉了`undefined`和`null`,这时`unknown`类型就不能赋值给`{}`类型后,类型`A`就为`string`。 + +另外,`strict`属性包含了`strictNullChecks`,如果打开`strict`属性,就相当于打开了`strictNullChecks`。 + ### strictPropertyInitialization `strictPropertyInitialization`设置类的实例属性都必须初始化,包括以下几种情况。 From 5361b8c9484da37e647681935236356f4b3a9509 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Thu, 3 Oct 2024 22:44:55 +0800 Subject: [PATCH 43/46] docs(declare): edit text --- docs/declare.md | 12 ++++++++++++ docs/tsconfig.json.md | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/declare.md b/docs/declare.md index ca7665a..a9b4bd0 100644 --- a/docs/declare.md +++ b/docs/declare.md @@ -252,6 +252,7 @@ const a:A = { x: 0, y: 0 }; 某些第三方模块,原始作者没有提供接口类型,这时可以在自己的脚本顶部加上下面一行命令。 ```typescript +// 语法 declare module "模块名"; // 例子 @@ -371,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 diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index a6b82db..4312c11 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -536,7 +536,9 @@ TypeScript 内置的类型描述文件,主要有以下一些,完整的清单 } ``` -它还可以使用通配符“*”。 +上面示例中,paths 设置的是执行`require('b')`时,即加载的是`./bar/b`。 + +它还可以使用通配符“*”,表明模块名与模块位置的对应关系。 ```typescript { From 635a5fadebf813bdc3feb582c310eccc46e1739c Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Sat, 12 Oct 2024 10:51:00 +0800 Subject: [PATCH 44/46] docs(tsconfig.json): fixed #124 --- docs/tsconfig.json.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/tsconfig.json.md b/docs/tsconfig.json.md index 4312c11..e94a7c2 100644 --- a/docs/tsconfig.json.md +++ b/docs/tsconfig.json.md @@ -679,7 +679,7 @@ let func:StringOrNumberFunc = fn; ### strictNullChecks -不打开`strictNullChecks`的情况下,变量可以设为`undefined`或`null`,而不管其类型是什么。 +不打开`strictNullChecks`的情况下,一个变量不管类型是什么,都可以赋值为`undefined`或`null`。 ```typescript // 不打开 strictNullChecks 的情况 @@ -689,9 +689,9 @@ x = undefined; // 不报错 x = null; // 不报错 ``` -上面示例中,变量`x`的类型是`number`,但是赋值为`undefined`或`null`都不会报错。这是为了继承 JavaScript 的设定:当变量没有赋值时,它的值就为`undefined`。 +上面示例中,不打开`strictNullChecks`时,变量`x`的类型是`number`,但是赋值为`undefined`或`null`都不会报错。这是为了继承 JavaScript 的设定:当变量没有赋值时,它的值就为`undefined`。 -一旦打开`strictNullChecks`,就相当于从变量的值里面,排除了`undefined`和`null`,除非变量的类型是这两种类型。 +一旦打开`strictNullChecks`,就使用严格类型,禁止变量赋值为`undefined`和`null`,除非变量原本就是这两种类型。它相当于从变量的值里面,排除了`undefined`和`null`。 ```typescript // 打开 strictNullChecks 的情况 @@ -701,6 +701,8 @@ x = undefined; // 报错 x = null; // 报错 ``` +上面示例中,打开`strictNullChecks`时,变量`x`作为`number`类型,就不能赋值为`undefined`和`null`。 + 下面是一个例子。 ```typescript @@ -709,9 +711,9 @@ x = null; // 报错 type A = unknown extends {} ? string : number; ``` -上面示例中,`{}`代表了 Object 类型,不打开`strictNullChecks`时,它包括了`undefined`和`null`,就相当于包括了所有类型的值,所以这时`unknown`类型可以赋值给`{}`类型,类型`A`就为`number`。打开`strictNullChecks`时,`{}`就排除掉了`undefined`和`null`,这时`unknown`类型就不能赋值给`{}`类型后,类型`A`就为`string`。 +上面示例中,`{}`代表了 Object 类型,不打开`strictNullChecks`时,它包括了`undefined`和`null`,就相当于包括了所有类型的值,所以这时`unknown`类型可以赋值给`{}`类型,类型`A`就为`string`。打开`strictNullChecks`时,`{}`就排除掉了`undefined`和`null`,这时`unknown`类型就不能赋值给`{}`类型后,类型`A`就为`number`。 -另外,`strict`属性包含了`strictNullChecks`,如果打开`strict`属性,就相当于打开了`strictNullChecks`。 +最后,`strict`属性包含了`strictNullChecks`,如果打开`strict`属性,就相当于打开了`strictNullChecks`。 ### strictPropertyInitialization From 1725cbbcd1cfbd9bf7110fd6ead3edb5ff9ed180 Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Tue, 15 Oct 2024 19:08:17 +0800 Subject: [PATCH 45/46] docs(operator/extends): fixed #125 --- docs/operator.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/operator.md b/docs/operator.md index 5416439..7a51d4b 100644 --- a/docs/operator.md +++ b/docs/operator.md @@ -378,18 +378,17 @@ type T2 = RegExp extends Animal ? number : string; 一般来说,调换`extends`两侧类型,会返回相反的结果。举例来说,有两个类`Cat`和`Animal`,前者是后者的子类型,那么`Cat extends Animal`就为真,而`Animal extends Cat`就为伪。 -如果需要判断的类型是一个联合类型,那么条件运算符会展开这个联合类型。 +如果对泛型使用 extends 条件运算,有一个地方需要注意。当泛型的类型参数是一个联合类型时,那么条件运算符会展开这个类型参数,即`T<A|B> = T<A> | T<B>`,所以 extends 对类型参数的每个部分是分别计算的。 ```typescript -(A|B) extends U ? X : Y +type Cond<T> = T extends U ? X : Y; -// 等同于 - -(A extends U ? X : Y) | -(B extends U ? X : Y) +type MyType = Cond<A|B>; +// 等同于 Cond<A> | Cond<B> +// 等同于 (A extends U ? X : Y) | (B extends U ? X : Y) ``` -上面示例中,`A|B`是一个联合类型,进行条件运算时,相当于`A`和`B`分别进行运算符,返回结果组成一个联合类型。 +上面示例中,泛型`Cond`的类型参数`A|B`是一个联合类型,进行条件运算时,相当于`A`和`B`分别进行条件运算,返回结果组成一个联合类型。也就是说,如果类型参数是联合类型,条件运算的返回结果依然是一个联合类型。 如果不希望联合类型被条件运算符展开,可以把`extends`两侧的操作数都放在方括号里面。 @@ -398,18 +397,18 @@ type T2 = RegExp extends Animal ? number : string; type ToArray<Type> = Type extends any ? Type[] : never; -// string[]|number[] +// 返回结果 string[]|number[] type T = ToArray<string|number>; // 示例二 type ToArray<Type> = [Type] extends [any] ? Type[] : never; -// (string | number)[] +// 返回结果 (string | number)[] type T = ToArray<string|number>; ``` -上面的示例一,传入`ToArray<Type>`的类型参数是一个联合类型,所以会被展开,返回的也是联合类型。示例二是`extends`两侧的运算数都放在方括号里面,所以传入的联合类型不会展开,返回的是一个数组。 +上面的示例一,泛型`ToArray<Type>`的类型参数`string|number`是一个联合类型,所以会被展开,返回的也是联合类型`string[]|number[]`。示例二是`extends`两侧的运算数都放在方括号里面,左侧是`[Type]`,右侧是`[any]`,这时传入的联合类型不会展开,返回的是一个数组`(string|number)[]`。 条件运算符还可以嵌套使用。 From 1f5e92981ab36601ee9b473642ba59f434a73e9d Mon Sep 17 00:00:00 2001 From: ruanyf <yifeng.ruan@gmail.com> Date: Tue, 31 Dec 2024 09:56:43 +0800 Subject: [PATCH 46/46] docs(operator): fixed typo --- docs/operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operator.md b/docs/operator.md index 7a51d4b..b19ec56 100644 --- a/docs/operator.md +++ b/docs/operator.md @@ -660,7 +660,7 @@ const palette: Record<Colors, string|RGB> = { const greenComponent = palette.green.substring(1, 6); // 报错 ``` -上面示例中,`palette.green`属性调用`substring()`方法会报错,原因是这个方法只有字符串才有,而`palette.green`的类型是`srting|RGB`,除了字符串,还可能是元组`RGB`,而元组并不存在`substring()`方法,所以报错了。 +上面示例中,`palette.green`属性调用`substring()`方法会报错,原因是这个方法只有字符串才有,而`palette.green`的类型是`string|RGB`,除了字符串,还可能是元组`RGB`,而元组并不存在`substring()`方法,所以报错了。 如果要避免报错,要么精确给出变量`palette`每个属性的类型,要么对`palette.green`的值进行类型缩小。两种做法都比较麻烦,也不是很有必要。