From fe8da65112730145ba287e7170f537fa34714ade Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 31 Jul 2023 12:20:00 +0800 Subject: [PATCH] docs: finish chapter d.ts --- chapters.yml | 1 + docs/conditional-types.md | 148 ------------ docs/d.ts.md | 481 ++++++++++++-------------------------- docs/global.d.ts.md | 30 --- docs/utility.md | 30 ++- 5 files changed, 178 insertions(+), 512 deletions(-) delete mode 100644 docs/conditional-types.md delete mode 100644 docs/global.d.ts.md diff --git a/chapters.yml b/chapters.yml index 137f46b..b75f3f5 100644 --- a/chapters.yml +++ b/chapters.yml @@ -17,6 +17,7 @@ - decorator.md: 装饰器 - decorator-legacy.md: 装饰器(旧语法) - declare.md: declare 关键字 +- d.ts.md: d.ts 类型声明文件 - operator.md: 运算符 - mapping.md: 类型映射 - utility.md: 类型工具 diff --git a/docs/conditional-types.md b/docs/conditional-types.md deleted file mode 100644 index 606b928..0000000 --- a/docs/conditional-types.md +++ /dev/null @@ -1,148 +0,0 @@ -# 条件类型 - -```typescript -T extends U ? X : Y -``` - -上面定义中,T、U、X、Y 代表任意类型。`T extends U`表示类型的测试条件,如果满足此条件,返回类型`X`,否则返回类型`Y`。 - -下面是一个例子。 - -```typescript -type NonNullable = T extends null | undefined ? never : T; -``` - -上面式子定义了一个范型`NonNullable`,用来检测某个类型是否非空。 - -```typescript -type EmailAddress = string | string[] | null | undefined; - -// 等同于 type NonNullableEmailAddress = string | string[]; -type NonNullableEmailAddress = NonNullable; -``` - -TypeScript 提供了一些预定义的条件类型,下面逐一介绍。 - -## `NonNullable` - -`NonNullable`从类型`T`里面过滤掉`null`和`undefined`。 - -```typescript -type NonNullable = T extends null | undefined ? never : T; -``` - -```typescript -type A = NonNullable; // boolean -type B = NonNullable; // number -type C = NonNullable; // string -type D = NonNullable; // never -``` - -## `Extract` - -```typescript -type Extract = T extends U ? T : never; -``` - -`Extract`类型表达式相当于提取功能,只要`T`符合`U`就返回`T`,否则就过滤掉。 - -```typescript -type A = Extract; // string[] -type B = Extract<(() => void) | null, Function>; // () => void -type C = Extract<200 | 400, 200 | 201>; // 200 -type D = Extract; // never -``` - -## `Exclude` - -`Exclude`相当于排除功能,只要`T`符合`U`就过滤掉,否则返回`T`。 - -```typescript -type Exclude = T extends U ? never : T; -``` - -```typescript -type A = Exclude; // string -type B = Exclude<(() => void) | null, Function>; // null -type C = Exclude<200 | 400, 200 | 201>; // 400 -type D = Exclude; // number -``` - -## `ReturnType` - -`ReturnType`提取函数的返回类型。 - -```typescript -type ReturnType any> = T extends ( - ...args: any[] -) => infer R - ? R - : any; -``` - -```typescript -type A = ReturnType<() => string>; // string -type B = ReturnType<() => () => any[]>; // () => any[] -type C = ReturnType; // number -type D = ReturnType; // boolean -``` - -## `Parameters` - -`Parameters`提供函数`T`的所有参数类型,它的返回值是一个`tuple`类型,或者`never`(如果 T 不是函数)。 - -```typescript -type Parameters any> = T extends ( - ...args: infer P -) => any - ? P - : never; -``` - -```typescript -type A = Parameters<() => void>; // [] -type B = Parameters; // [any] -type C = Parameters; // [string, (number | undefined)?] -type D = Parameters; // number[] -``` - -`Array.isArray()`只有一个参数,所以返回的类型是`[any]`,而不是`any[]`。`Math.max()`的参数是任意多个数值,而不是一个数值数组,所以返回的类型是`number[]`,而不是`[number[]]`。 - -## `ConstructorParameters` - -`ConstructorParameters`提取一个构造函数的所有参数类型。它的返回值是一个 tuple 类型,成员是所有参数的类型,如果 T 不是函数,则返回 never。 - -```typescript -type ConstructorParameters< - T extends new (...args: any[]) => any -> = T extends new (...args: infer P) => any ? P : never; -``` - -```typescript -type A = ConstructorParameters; -// [(string | undefined)?] - -type B = ConstructorParameters; -// string[] - -type C = ConstructorParameters; -// [string, (string | undefined)?] -``` - -## `InstanceType` - -`InstanceType`提取构造函数的返回值的类型,等同于构造函数的`ReturnType`。 - -```typescript -type InstanceType any> = T extends new ( - ...args: any[] -) => infer R - ? R - : any; -``` - -```typescript -type A = InstanceType; // Error -type B = InstanceType; // Function -type C = InstanceType; // RegExp -``` diff --git a/docs/d.ts.md b/docs/d.ts.md index 578140f..3505316 100644 --- a/docs/d.ts.md +++ b/docs/d.ts.md @@ -2,19 +2,19 @@ ## 简介 -模块需要提供一个类型声明文件(declaration file),让模块使用者了解它的接口类型。 +单独使用的模块,一般会同时提供一个单独的类型声明文件(declaration file),把本模块的外部接口的所有类型都写在这个文件里面,便于模块使用者了解接口,也便于编译器检查使用者的用法是否正确。 -类型声明文件就是接口的类型描述,写在一个单独的文件里面。它里面只有类型代码,没有具体的代码实现。 - -它的文件名一般为`[模块名].d.ts`的形式,其中的`d`表示 declaration(声明)。 +类型声明文件里面只有类型代码,没有具体的代码实现。它的文件名一般为`[模块名].d.ts`的形式,其中的`d`表示 declaration(声明)。 举例来说,有一个模块的代码如下。 ```typescript const maxInterval = 12; + function getArrayLength(arr) { return arr.length; } + module.exports = { getArrayLength, maxInterval, @@ -28,7 +28,18 @@ export function getArrayLength(arr: any[]): number; export const maxInterval: 12; ``` -如果输出的是一个值,那么类型声明文件需要使用`export default`或`export=`。 +类型声明文件也可以使用`export =`命令,输出对外接口。下面是 moment 模块的类型声明文件的例子。 + +```typescript +declare module 'moment' { + function moment(): any; + export = moment; +} +``` + +上面示例中,模块`moment`内部有一个函数`moment()`,而`export =`表示`module.exports`输出的就是这个函数。 + +除了使用`export =`,模块输出在类型声明文件中,也可以使用`export default`表示。 ```typescript // 模块输出 @@ -44,7 +55,9 @@ declare const pi: number; export= pi; ``` -下面是一个简单例子,有一个类型声明文件`types.d.ts`。 +上面示例中,模块输出的是一个整数,那么可以用`export default`或`export =`表示输出这个值。 + +下面是一个如何使用类型声明文件的简单例子。有一个类型声明文件`types.d.ts`。 ```typescript // types.d.ts @@ -58,7 +71,7 @@ export interface Character { ```typescript // index.ts -import { Character } from "./types.d.ts"; +import { Character } from "./types"; export const character:Character = { catchphrase: "Yee-haw!", @@ -66,7 +79,7 @@ export const character:Character = { }; ``` -定义了类型声明文件以后,可以将其包括在项目的 tsconfig.json 文件里面,方便打包和其他脚本加载。比如,moment 模块的类型声明文件是`moment.d.ts`,将其加入 tsconfig.json。 +类型声明文件也可以包括在项目的 tsconfig.json 文件里面,这样的话,编译器打包项目时,会自动将类型声明文件加入编译,而不必在每个脚本里面加载类型声明文件。比如,moment 模块的类型声明文件是`moment.d.ts`,使用 moment 模块的项目可以将其加入项目的 tsconfig.json 文件。 ```typescript { @@ -78,26 +91,37 @@ export const character:Character = { } ``` -有些模块是 CommonJS 格式,采用`module.exports`输出接口。它的类型描述文件可以写成下面的形式。 - -```typescript -declare module 'moment' { - function moment(): any; - export = moment; -} -``` - -上面示例中,模块`moment`是 CommonJS 格式,它的内部有一个函数`moment()`,而`export =`表示`module.exports`输出的就是这个函数。 +## 类型声明文件的来源 类型声明文件主要有以下三种来源。 -- TypeScript 语言内置的类型声明文件。 -- 第三方类型声明文件,需要自己安装。 -- 自己编写的类型声明文件。 +- TypeScript 编译器自动生成。 +- TypeScript 内置类型文件。 +- 外部模块的类型声明文件,需要自己安装。 + +### 自动生成 + +只要使用编译选项`declaration`,编译器就会在编译时自动生成单独的类型声明文件。 + +下面是在`tsconfig.json`文件里面,打开这个选项。 + +```typescript +{ + "compilerOptions": { + "declaration": true + } +} +``` + +你也可以在命令行打开这个选项。 + +```bash +$ tsc --declaration +``` ### 内置声明文件 -安装 TypeScript 语言时,会同时安装一些内置声明文件,主要是 JavaScript 语言接口和运行环境 API 的类型声明。 +安装 TypeScript 语言时,会同时安装一些内置的类型声明文件,主要是内置的全局对象(JavaScript 语言接口和运行环境 API)的类型声明。 这些内置声明文件位于 TypeScript 语言安装目录的`lib`文件夹内,数量大概有几十个,下面是其中一些主要文件。 @@ -114,9 +138,23 @@ declare module 'moment' { 这些内置声明文件的文件名统一为“lib.[description].d.ts”的形式,其中`description`部分描述了文件内容。比如,`lib.dom.d.ts`这个文件就描述了 DOM 结构的类型。 -TypeScript 编译器会自动加载这些内置声明文件,所以不需要特别的配置。 +如果开发者想了解全局对象的类型接口(比如 ES6 全局对象的类型),那么就可以去查看这些内置声明文件。 -### 第三方声明文件 +TypeScript 编译器会自动根据编译目标`target`的值,加载对应的内置声明文件,所以不需要特别的配置。但是,可以使用编译选项`lib`,指定加载哪些内置声明文件。 + +```javascript +{ + "compilerOptions": { + "lib": ["dom", "es2021"] + } +} +``` + +上面示例中,`lib`选项指定加载`dom`和`es2021`这两个内置类型声明文件。 + +编译选项`noLib`会禁止加载任何内置声明文件。 + +### 外部类型声明文件 如果项目中使用了外部的某个第三方代码库,那么就需要这个库的类型声明文件。 @@ -124,27 +162,33 @@ TypeScript 编译器会自动加载这些内置声明文件,所以不需要特 (1)这个库自带了类型声明文件。 +一般来说,如果这个库的源码包含了`[vendor].d.ts`文件,那么就自带了类型声明文件。其中的`vendor`表示这个库的名字,比如`moment`这个库就自带`moment.d.ts`。使用这个库可能需要单独加载它的类型声明文件。 + (2)这个库没有自带,但是可以找到社区制作的类型声明文件。 -(3)找不到类型声明文件,需要自己写。 - -一般来说,如果这个库的源码包含了`[vendor].d.ts`文件,那么就自带了类型声明文件。其中的`vendor`表示这个库的名字,比如`moment`这个库就自带`moment.d.ts`。 - -### DefinitelyTyped 社区 - 第三方库如果没有提供类型声明文件,社区往往会提供。TypeScript 社区主要使用 [DefinitelyTyped 仓库](https://github.com/DefinitelyTyped/DefinitelyTyped),各种类型声明文件都会提交到那里,已经包含了几千个第三方库。 -TypeScript 官网有 DefinitelyTyped 的搜索入口,需要第三方声明文件的时候,就可以去 [www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) 搜搜看。 - -这些声明文件都会发布到 npm 的`@types`名称空间之下。比如,jQuery 的类型声明文件就放在`@types/jquery`这个库,安装这个库就可以了。 +这些声明文件都会作为一个单独的库,发布到 npm 的`@types`名称空间之下。比如,jQuery 的类型声明文件就发布成`@types/jquery`这个库,使用时安装这个库就可以了。 ```bash $ npm install @types/jquery --save-dev ``` -执行上面的命令,`@types/jquery`这个库就安装到项目的`node_modules/@types/jquery`目录,里面的`index.d.ts`文件就是 jQuery 的类型声明文件。 +执行上面的命令,`@types/jquery`这个库就安装到项目的`node_modules/@types/jquery`目录,里面的`index.d.ts`文件就是 jQuery 的类型声明文件。如果类型声明文件不是`index.d.ts`,那么就需要在`package.json`的`types`或`typings`字段,指定类型声明文件的文件名。 -然后,在`tsconfig.json`文件里面加上类型声明文件的位置。 +TypeScript 会自动加载`node_modules/@types`目录下的模块,但可以使用编译选项`typeRoots`改变这种行为。 + +```typescript +{ + "compilerOptions": { + "typeRoots": ["./typings", "./vendor/types"] + } +} +``` + +上面示例表示,TypeScript 不再去`node_modules/@types`目录,而是去跟当前`tsconfig.json`同级的`typings`和`vendor/types`子目录,加载类型模块了。 + +默认情况下,TypeScript 会自动加载`typeRoots`目录里的所有模块,编译选项`types`可以指定加载哪些模块。 ```javascript { @@ -154,30 +198,48 @@ $ npm install @types/jquery --save-dev } ``` -上面设置中,`types`属性是一个数组,成员是所要加载的类型声明文件,要加载几个文件,就有几个成员,每个成员在子目录`node_modules/@types`下面都有一个自己的目录。 +上面设置中,`types`属性是一个数组,成员是所要加载的类型模块,要加载几个模块,这个数组就有几个成员,每个类型模块在`typeRoots`目录下都有一个自己的子目录。这样的话,TypeScript 就会自动去`jquery`子目录,加载 jQuery 的类型声明文件。 -这样的话,你的项目加载 jQuery 时,编译器就会正确加载它的类型声明文件。 +(3)找不到类型声明文件,需要自己写。 + +有时实在没有第三方库的类型声明文件,又很难完整给出该库的类型描述,这时你可以告诉 TypeScript 相关对象的类型是`any`。比如,使用 jQuery 的脚本可以写成下面这样。 + +```typescript +declare var $:any + +// 或者 +declare type JQuery = any; +declare var $:JQuery; +``` + +上面代码表示,jQuery 的`$`对象是外部引入的,类型是`any`,也就是 TypeScript 不用对它进行类型检查。 + +也可以采用下面的写法,将整个外部模块的类型设为`any`。 + +```typescript +declare module '模块名'; +``` + +有了上面的命令,指定模块的所有接口都将视为`any`类型。 ## declare 关键字 -类型声明文件只包含类型描述,不包含具体实现,所以非常适合使用 declare 语句来描述类型。 +类型声明文件只包含类型描述,不包含具体实现,所以非常适合使用 declare 语句来描述类型。declare 关键字的具体用法,详见《declare 关键字》一章,这里讲解如何在类型声明文件里面使用它。 -declare 字的具体用法,详见《declare 关键字》一章,这里讲解如何在类型声明文件里面使用它。 - -类型声明文件里面,变量的类型描述必须使用`declare`命令,否则会报错。 +类型声明文件里面,变量的类型描述必须使用`declare`命令,否则会报错,因为变量声明语句是值相关代码。 ```typescript -declare let foo:string; +declare let foo:string; ``` -interface 类型有没有`declare`都可以。 +interface 类型有没有`declare`都可以,因为 interface 是完全的类型代码。 ```typescript interface Foo {} // 正确 declare interface Foo {} // 正确 ``` -类型声明文件里面,可以使用`export`命令。 +类型声明文件里面,顶层可以使用`export`命令,也可以不用,除非使用者脚本会显式使用`export`命令输入类型。 ```typescript export interface Data { @@ -185,16 +247,18 @@ export interface Data { } ``` -下面是 moment 模块的类型描述文件`moment.d.ts`的例子。 +下面是类型声明文件的一些例子。先看 moment 模块的类型描述文件`moment.d.ts`。 ```typescript declare module 'moment' { export interface Moment { - format(format:string):string; + format(format:string): string; + add( - amount:number, - unit:'days' | 'months' | 'years' - ):Moment; + amount: number, + unit: 'days' | 'months' | 'years' + ): Moment; + subtract( amount:number, unit:'days' | 'months' | 'years' @@ -202,14 +266,16 @@ declare module 'moment' { } function moment( - input?:string | Date - ):Moment; + input?: string | Date + ): Moment; export default moment; } ``` -下面是 D3 库的`D3.d.ts`文件。 +上面示例中,可以注意一下默认接口`moment()`的写法。 + +下面是 D3 库的类型声明文件`D3.d.ts`。 ```typescript declare namespace D3 { @@ -219,20 +285,23 @@ declare namespace D3 { (element: EventTarget): Selection; }; } + export interface Event { x: number; y: number; } + export interface Base extends Selectors { event: Event; } } + declare var d3: D3.Base; -``` +``` ## 模块发布 -类型声明文件写好后,如果要在 npm 上面发布,可以在 package.json 文件添加一个`types`字段,指明类型声明文件的位置。 +当前模块如果包含自己的类型声明文件,可以在 package.json 文件里面添加一个`types`字段或`typings`字段,指明类型声明文件的位置。 ```typescript { @@ -246,9 +315,7 @@ declare var d3: D3.Base; 上面示例中,`types`字段给出了类型声明文件的位置。 -注意,`types`字段也可以写成`typings`。 - -另外,如果类型声明文件为`index.d.ts`,且在项目的根目录(与`index.js`在一起),那么不需要注明`types`字段。 +注意,如果类型声明文件名为`index.d.ts`,且在项目的根目录中,那就不需要在`package.json`里面注明了。 有时,类型声明文件会单独发布成一个 npm 模块,这时用户就必须同时加载该模块。 @@ -267,15 +334,22 @@ declare var d3: D3.Base; } ``` -上面示例是一个模块的 package.json 文件,该文件需要 browserify 模块。由于后者的类型声明文件放在另一个模块`@types/browserify`,所以还必需加载那个模块。 +上面示例是一个模块的 package.json 文件,该模块需要 browserify 模块。由于后者的类型声明文件是一个单独的模块`@types/browserify`,所以还需要加载那个模块。 ## 三斜杠命令 -其他脚本可以使用三斜杠命令,加载类型声明文件。 +如果类型声明文件的内容非常多,可以拆分成多个文件,然后入口文件使用三斜杠命令,加载其他拆分后的文件。 -三斜杠命令(`///`)是一个编译器命令,用来指定编译器行为。它只能用在文件的头部,如果用在其他地方,会被当作普通的注释。 +举例来说,入口文件是`main.d.ts`,里面的接口定义在`interfaces.d.ts`,函数定义在`functions.d.ts`。那么,`main.d.ts`里面可以用三斜杠命令,加载后面两个文件。 -若一个文件中使用了三斜线命令,那么在三斜线命令之前只允许使用单行注释、多行注释和其他三斜线命令,否则三斜杠命令会被当作普通的注释。 +```typescript +/// +/// +``` + +三斜杠命令(`///`)是一个 TypeScript 编译器命令,用来指定编译器行为。它只能用在文件的头部,如果用在其他地方,会被当作普通的注释。另外,若一个文件中使用了三斜线命令,那么在三斜线命令之前只允许使用单行注释、多行注释和其他三斜线命令,否则三斜杠命令也会被当作普通的注释。 + +除了拆分类型声明文件,三斜杠命令也可以用于普通脚本加载类型声明文件。 三斜杠命令主要包含三个参数,代表三种不同的命令。 @@ -290,12 +364,20 @@ declare var d3: D3.Base; `/// `是最常见的三斜杠命令,告诉编译器在编译时需要包括的文件,常用来声明当前脚本依赖的类型文件。 ```typescript -/// +/// let count = add(1, 2); ``` -上面示例中,编译当前脚本时,还会同时编译`lib.ts`。编译产物会有两个 JS 文件,一个当前脚本,另一个就是`lib.js`。 +上面示例表示,当前脚本依赖于`./lib.ts`,里面是`add()`的定义。编译当前脚本时,还会同时编译`./lib.ts`。编译产物会有两个 JS 文件,一个当前脚本,另一个就是`./lib.js`。 + +下面的例子是当前脚本依赖于 Node.js 类型声明文件。 + +```typescript +/// +import * as URL from "url"; +let myUrl = URL.parse("https://www.typescriptlang.org"); +``` 编译器会在预处理阶段,找出所有三斜杠引用的文件,将其添加到编译列表中,然后一起编译。 @@ -306,7 +388,7 @@ let count = add(1, 2); - `path`参数必须指向一个存在的文件,若文件不存在会报错。 - `path`参数不允许指向当前文件。 -默认情况下,每个三斜杠命令引入的脚本,都会编译成单独的 JS 文件。如果希望编译后只产出一个合并文件,可以使用编译参数`outFile`。但是,`outFile`编译参数不支持合并 CommonJS 模块和 ES 模块,只有当编译参数`module`的值设为 None、System 或 AMD 时,才能编译成一个文件。 +默认情况下,每个三斜杠命令引入的脚本,都会编译成单独的 JS 文件。如果希望编译后只产出一个合并文件,可以使用编译选项`outFile`。但是,`outFile`编译选项不支持合并 CommonJS 模块和 ES 模块,只有当编译参数`module`的值设为 None、System 或 AMD 时,才能编译成一个文件。 如果打开了编译参数`noResolve`,则忽略三斜杠指令。将其当作一般的注释,原样保留在编译产物中。 @@ -324,15 +406,13 @@ types 参数的值是类型库的名称,也就是安装到`node_modules/@types 可以看到,这个命令的作用类似于`import`命令。 -注意,这个命令只在你自己手写`.d.ts`文件时,才有必要用到,也就是说,只应该用在`.d.ts`文件中,普通的`.ts`脚本文件不需要写这个命令。 - -我们应该只在类型声明文件(.d.ts)中使用“/// ”三斜线命令,而不应该在普通的`.ts`脚本文件中使用该命令。如果是普通的`.ts`脚本,可以使用`tsconfig.json`文件的`types`属性指定依赖的类型库。 +注意,这个命令只在你自己手写类型声明文件(`.d.ts`文件)时,才有必要用到,也就是说,只应该用在`.d.ts`文件中,普通的`.ts`脚本文件不需要写这个命令。如果是普通的`.ts`脚本,可以使用`tsconfig.json`文件的`types`属性指定依赖的类型库。 ### `/// ` `/// `命令允许脚本文件显式包含内置 lib 库,等同于在`tsconfig.json`文件里面使用`lib`属性指定 lib 库。 -前文说过,安装 TypeScript 软件包时,会同时安装一些内置的类型声明文件,即内置的 lib 库。这些库文件位于 TypeScript 安装目录的`lib`文件夹中,它们描述了 JavaScript 语言的标准 API。 +前文说过,安装 TypeScript 软件包时,会同时安装一些内置的类型声明文件,即内置的 lib 库。这些库文件位于 TypeScript 安装目录的`lib`文件夹中,它们描述了 JavaScript 语言和引擎的标准 API。 库文件并不是固定的,会随着 TypeScript 版本的升级而更新。库文件统一使用“lib.[description].d.ts”的命名方式,而`/// `里面的`lib`属性的值就是库文件名的`description`部分,比如`lib="es2015"`就表示加载库文件`lib.es2015.d.ts`。 @@ -342,268 +422,3 @@ types 参数的值是类型库的名称,也就是安装到`node_modules/@types 上面示例中,`es2017.string`对应的库文件就是`lib.es2017.string.d.ts`。 -## 自定义类型声明文件 - -有时实在没有第三方库的类型声明文件,你可以告诉 TypeScript 相关对象的类型是`any`。比如,使用 jQuery 的脚本可以写成下面这样。 - -```typescript -declare var $: any -``` - -上面代码表示,jQuery 的`$`对象是外部引入的,类型是`any`,也就是 TypeScript 不用对它进行类型检查。 - - - -为了描述不是用 TypeScript 编写的库的形状,我们需要声明库公开的 API。通常,这些是在.d.ts文件中定义的。如果您熟悉 C/C++,您可以将这些视为.h文件。 - -在 Node.js 中,大多数任务都是通过加载一个或多个模块来完成的。我们可以为每个模块,定义一个自己的 .d.ts 文件,但是把所有模块的类型定义放在一个大的 .d.ts 文件更方便。 - - - -开源库 [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) 提供大部分常用第三方库的类型,在写自己的`[vendor.d.ts]`之前,可以先到这个库查看,它有没有提供。下面是 node.d.ts 的简化的样子。 - -```typescript -declare module "url" { - export interface Url { - protocol?: string; - hostname?: string; - pathname?: string; - } - export function parse( - urlStr: string, - parseQueryString?, - slashesDenoteHost? - ): Url; -} -declare module "path" { - export function normalize(p: string): string; - export function join(...paths: any[]): string; - export var sep: string; -} -``` - -后面就可以在脚本里面,使用`/// node.d.ts`给出类型定义。 - -```typescript -/// -``` - -```typescript -/// -import * as URL from "url"; -let myUrl = URL.parse("https://www.typescriptlang.org"); -``` - -有时候,很难为别人的模块写出完整的 .d.ts 类型定义文件。TypeScript 这时允许在 .d.ts 里面只写模块名,不写具体的类型定义。 - -```typescript -declare module "hot-new-module"; -``` - -脚本加载这个模块以后,所有引入的接口都是 any 类型。 - -```typescript -import x, { y } from "hot-new-module"; -x(y); -``` - -d.ts 文件里面,需要声明引入变量的类型。比如,jQuery 可以这样声明。 - -```typescript -declare var $: any; -``` - -上面示例表示变量`$`可以是任意类型。 - -也可以像下面这样,自定义一个 JQuery 类型。 - -```typescript -declare type JQuery = any; -declare var $: JQuery; -``` - -另一个方法是声明一个模块 jquery。 - -```typescript -declare module "jquery"; -``` - -然后在脚本里面加载这个模块。 - -```typescript -import * as $ from "jquery"; -``` - -下面是 Node.js 的 Process 模块的例子。 - -```typescript -interface Process { - exit(code?: number): void; -} -declare var process: Process; -``` - -如果你要自己为 Process 对象添加一个方法`exitWithLogging()`,就需要自己补上该方法的类型注释。 - -```typescript -interface Process { - exitWithLogging(code?: number): void; -} -process.exitWithLogging = function() { - console.log("exiting"); - process.exit.apply(process, arguments); -}; -``` - -## package.json - -TypeScript扩展了“package.json”文件,增加了typings属性和types属性。虽然两者的名字不同,但是作用相同,它们都用于指定当前npm包提供的声明文件。 - -```typescript -{ - "name": "my-package", - "version": "1.0.0", - "main": "index.js", - "typings": "index.d.ts" -} -``` - -此例中,使用typings属性定义了“my-package”包的声明文件为“index.d.ts”文件。当TypeScript编译器进行模块解析时,将会读取该属性的值并使用指定的“index.d.ts”文件作为声明文件。这里我们也可以将typings属性替换为types属性,两者是等效的。 - -如果一个npm包的声明文件为“index.d.ts”且位于npm包的根目录下,那么在“package.json”文件中也可以省略typings属性和types属性,因为编译器在进行模块解析时,若在“package.json”文件中没有找到typings属性和types属性,则将默认使用名为“index.d.ts”的文件作为声明文件。 - -在TypeScript 3.1版本中,编译器能够根据当前安装的TypeScript版本来决定使用的声明文件,该功能是通过“package.json”文件中的typesVersions属性来实现的。 - -```javascript -{ - "name": "my-package", - "version": "1.0.0", - "main": "index.js", - "typings": "index.d.ts", - "typesVersions": { - ">=3.7": { - "*": ["ts3.7/*"] - }, - ">=3.1": { - "*": ["ts3.1/*"] - } - } -} -``` - -此例中,我们定义了两个声明文件匹配规则: - -▪第7行,当安装了TypeScript 3.7及以上版本时,将使用“ts3.7”目录下的声明文件。 - -▪第10行,当安装了TypeScript 3.1及以上版本时,将使用“ts3.1”目录下的声明文件。 - -需要注意的是,typesVersions中的声明顺序很关键,编译器将从第一个声明(此例中为">=3.7")开始尝试匹配,若匹配成功,则应用匹配到的值并退出。因此,若将此例中的两个声明调换位置,则会产生不同的结果。 - -此外,如果typesVersions中不存在匹配的版本,如当前安装的是TypeScript 2.0版本,那么编译器将使用typings属性和types属性中定义的声明文件。 - -## @types - -TypeScript 官方提供了加载许多常用模块的类型注释,都放在 NPM 仓库的 @types 名称空间下面。 - -你可以像安装 npm 模块一样,安装外部库的类型注释。 - -```bash -$ npm install @types/jquery --save-dev -``` - -`@types/jquery`里面包括了模块类型和全局变量的类型。 - -你可以直接使用下面的语句。 - -```typescript -import * as $ from "jquery"; -``` - -## 自定义声明文件 - -如果使用的第三方代码库没有提供内置的声明文件,而且在DefinitelyTyped仓库中也没有对应的声明文件,那么就需要开发者自己编写一个声明文件。 - -如果我们不想编写一个详尽的声明文件,而只是想要跳过对某个第三方代码库的类型检查,则可以使用下面介绍的方法。 - -如果为 jQuery 创建一个“.d.ts”声明文件,例如“jquery.d.ts”。“jquery.d.ts”声明文件的内容如下: - -```typescript -declare module 'jquery'; -``` - -此例中的代码是外部模块声明,该声明会将jquery模块的类型设置为any类型。jquery模块中所有成员的类型都成了any类型,这等同于不对jQuery进行类型检查。 - -“typings.d.ts”文件的内容如下: - -```typescript -declare module 'mod' { - export function add(x: number, y: number): number; -} -``` - -在“a.ts”文件中,可以使用非相对模块导入语句来导入外部模块“mod”。示例如下: - -```typescript -import * as Mod from 'mod'; - -Mod.add(1, 2); -``` - -## es6-shim.d.ts - -如果代码编译成 ES5(`tsconfig.json`设成`"target": ES5`),但是代码会用到 ES6 的 API,并且希望 IDE 能够正确识别,可以引入`es6-shim.d.ts`。 - -```bash -$ npm install @types/es6-shim -D -``` - -`tsconfig.json`加入下面的设置。 - -```javascript -"types" : ["jquery", "es6-shim"] -``` - -另外一个新的垫片库是`core-js`。 - -## reference 命令 - -自己以前的项目可以自定义一个类型声明文件,比如`typings.d.ts`。 - -比如,你以前写过一个函数。 - -```typescript -function greeting(name) { - console.log("hello " + name); -} -``` - -新项目要用到这个函数,你可以为这个函数单独写一个类型文件`src/typings.d.ts`。 - -```typescript -export function greeting(name: string): void; -``` - -然后,需要在用到这个库的脚本头部加上一行,用三斜杠语法告诉 TypeScript 类型声明文件的位置。 - -```typescript -/// -``` - -如果类型声明文件是随 NPM 安装的,那么`reference`语句的属性需要从`path`改成`type`。 - -```typescript -/// -``` - -## JavaScript 项目加入 TypeScript - -如果现有的 JavaScript 项目需要加入 TypeScript,可以在`tsconfig.json`文件加入`"allowJs": true`设置,表示将 JS 文件一起复制到编译产物目录。 - -这时,TypeScript 不会对 JavaScript 脚本进行类型检查。如果你希望也进行类型检查,可以设置`"checkJs": true`。 - -另一种方法是在 JavaScript 脚本的第一行,加上注释`//@ts-check`,这时 TypeScript 也会对这个脚本进行检查。 - -打开`"checkJs": true`以后,如果不希望对有的 JavaScript 脚本进行类型检查,可以在该脚本头部加上`//@ts-ignore`。 - -You can also help tsc with type inference by adding the JSDoc annotations (such as -@param and @return) to your JavaScript code. diff --git a/docs/global.d.ts.md b/docs/global.d.ts.md deleted file mode 100644 index 319ef71..0000000 --- a/docs/global.d.ts.md +++ /dev/null @@ -1,30 +0,0 @@ -# global.d.ts - -`global.d.ts`文件用来放置全局的 interfaces 和类型。一旦设置,该项目的所有文件,都可以读取这些类型。 - - -在这个文件里面可以全局申明模块。 - -```typescript -// global.d.ts -declare module 'foo' { - // Some variable declarations - export var bar: number; /*sample*/ -} -``` - -然后可以用模块名输入。 - -```typescript -// anyOtherTsFileInYourProject.ts -import * as foo from 'foo'; -// TypeScript assumes (without doing any lookup) that -// foo is {bar:number} -``` - -该文件的另一个用途是声明一些编译时常量。 - -```typescript -declare const BUILD_MODE_PRODUCTION: boolean; // can be used for conditional compiling -declare const BUILD_VERSION: string; -``` \ No newline at end of file diff --git a/docs/utility.md b/docs/utility.md index 7168ef2..62bdd64 100644 --- a/docs/utility.md +++ b/docs/utility.md @@ -111,6 +111,10 @@ type ConstructorParameters< type T1 = Exclude<'a'|'b'|'c', 'a'>; // 'b'|'c' type T2 = Exclude<'a'|'b'|'c', 'a'|'b'>; // 'c' type T3 = Exclude void), Function>; // string +type T4 = Exclude; // string +type T5 = Exclude<(() => void) | null, Function>; // null +type T6 = Exclude<200 | 400, 200 | 201>; // 400 +type T7 = Exclude; // number ``` `Exclude`的实现如下。 @@ -129,6 +133,9 @@ type Exclude = T extends U ? never : T; type T1 = Extract<'a'|'b'|'c', 'a'>; // 'a' type T2 = Extract<'a'|'b'|'c', 'a'|'b'>; // 'a'|'b' type T3 = Extract<'a'|'b'|'c', 'a'|'d'>; // 'a' +type T4 = Extract; // string[] +type T5 = Extract<(() => void) | null, Function>; // () => void +type T6 = Extract<200 | 400, 200 | 201>; // 200 ``` 如果参数类型`Union`不包含在联合类型`UnionType`之中,则返回`never`类型。 @@ -145,7 +152,7 @@ type Extract = T extends U ? T : never; ## `InstanceType` -`InstanceType`的参数`Type`是一个构造函数,返回该构造函数对应的实例类型。 +`InstanceType`提取构造函数的返回值的类型(即实例类型),参数`Type`是一个构造函数,等同于构造函数的`ReturnType`。 ```typescript type T = InstanceType< @@ -155,6 +162,16 @@ type T = InstanceType< 上面示例中,类型参数是一个构造函数`new () => object`,返回值是该构造函数的实例类型(`object`)。 +下面是一些例子。 + +```typescript +type A = InstanceType; // Error +type B = InstanceType; // Function +type C = InstanceType; // RegExp +``` + +上面示例中,`InstanceType`的参数都是 TypeScript 内置的原生对象的构造函数类型,`InstanceType`的返回值就是这些构造函数的实例类型。 + 由于 Class 作为类型,代表实例类型。要获取它的构造方法,必须把它当成值,然后用`typeof`运算符获取它的构造方法类型。 ```typescript @@ -203,6 +220,11 @@ type T1 = NonNullable; // string[] type T2 = NonNullable; + +type T3 = NonNullable; // boolean +type T4 = NonNullable; // number +type T5 = NonNullable; // string +type T6 = NonNullable; // never ``` `NonNullable`的实现如下。 @@ -574,6 +596,12 @@ type T2 = ReturnType<() => { }>; // { a: string; b: number } type T3 = ReturnType<(s:string) => void>; // void + +type T4 = ReturnType<() => () => any[]>; // () => any[] + +type T5 = ReturnType; // number + +type T6 = ReturnType; // boolean ``` 如果参数类型是泛型函数,返回值取决于泛型类型。如果泛型不带有限制条件,就会返回`unknown`。