前言
关键字declaration、ts-node、compilerOptions.typeRoots、lodash、
参考资料
compilerOptions.declaration:链接- 「TypeScript 入门教程」声明文件:链接
- lodash 官方文档:链接
- 「简书」TS 的三斜线指令:链接
- Triple-Slash Directives:链接
notes
创建声明文件,可以是
- 自动生成
- 手动编写
在声明文件中不允许出现初始化表达式,也就是不能出现赋值语句。
自动生成
自动生成声明文件的前提
- 工程是使用 ts 来开发的
- 将配置文件
tsconfig.json中的compilerOptions.declaration设置为 true
工程发布(编译)后,原来的 .ts 文件会变为 .js 文件。
原来写在 .ts 文件中的类型约束,在生成的发布结果中默认是不存在的。
但是如果满足上面提到的两个前提,那么在编译后会多生成一个 .d.ts 声明文件。
如果我们发布的文件需要被其他开发者使用,建议开启 compilerOptions.declaration,这么做能够更好地描述发布结果中的类型,让使用者清楚它们访问的成员的类型信息。
手动编写
🤔 什么情况下,需要我们手动编写声明文件?
答:没有提供声明文件的 js 库。
对于常用的库,它们一般都是具备类型声明文件的,这点我们无需担心。 常用的库,比如:axios、jquery、lodash、mock、moment 等等。
全局声明
声明一些全局的对象、属性、变量
全局变量的声明文件主要有以下几种语法:
- declare var 声明全局变量
- declare function 声明全局方法
- declare class 声明全局类
- declare enum 声明全局枚举类型
- declare namespace 声明(含有子属性的)全局对象
- interface 和 type 声明全局类型
模块声明
- declare module 扩展模块
三斜线指令
/// <reference path="../../index.d.ts" />
三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。 三斜线指令仅可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。
codes
在学习 2. 在node中搭建TS开发环境 时,介绍如何搭建一个简单的 ts 开发环境。
使用npm i -D @types/node来安装 node 环境下的一些声明文件,现在我们尝试将 @types/node目录给删掉,尝试自己写声明文件。

console.log('test');
当我们将 node 目录给删除后,node 的声明文件就没有了。
此时对 console 变量的类型声明信息也没了,所以我们在 index.ts 中书写 console.log('test')是会报错的。
下面尝试在全局的声明文件global.d.ts中添加 console 的声明信息。
declare var console: {log(message?: any): void;};
interface Console {log(message?: any): void;}declare var console: Console;
declare namespace console {function log(message?: any): void;}
namespace表示命名空间,可以将其认为是一个对象,命名空间中的内容,必须通过命名空间.成员名访问。
添加 setTimeout、setInterval 的声明信息。
declare namespace console {function log(message?: any): void;}type timeHandler = () => void;declare function setTimeout(handler: timeHandler, miliseconds: number): number;declare function setInterval(handler: timeHandler, miliseconds: number): number;
console.log("test");setTimeout(() => {console.log('123');}, 1000);setInterval(() => {console.log('abc');}, 1000);
当我们在全局声明文件中添加上 console、setTimeout、setInterval的类型信息后,再 index.ts 中访问它们,就不会报错了。
const _ = require("lodash");console.log(_.chunk); // => [Function: chunk]
import * as _ from "lodash"; // ×// => Could not find a declaration file for module 'lodash'.
npm i lodash
先提前安装 lodash
require("lodash")
注意此时只能通过 node 模块化标准 commonjs 模块化写法来导入 lodash
由于咋们安装的 lodash 库,它是使用 js 写的,仅通过上述方式安装,在 .ts 文件中书写代码时没法给我们提供智能提示。
import * as _ from "lodash"
如果使用 es module 模块化写法来导入 lodash,会报错:无法找到 lodash 的声明文件。
🤔 如何安装 lodash 的声明文件?
答:npm i @types/lodash

对于常见的库 xxx,我们都可以通过安装 @types/xxx 来获取到它的声明文件
但是对于一些没有提供声明文件的库,或者是我们自己写的库,那我们就得手动编写声明文件,下面介绍模块化声明该如何写。
// 模块声明文件declare module "lodash" {export function chunk(arr: any[], size: number): any[][];}
import * as _ from "lodash";console.log(_.chunk(["a", "b", "c", "d"], 2));// => [['a', 'b'], ['c', 'd']]
{"devDependencies": {"@types/node": "^17.0.23"},"scripts": {"dev": "nodemon --watch src -e ts --exec ts-node ./src/index.ts","build": "rm -rf dist && tsc"},"dependencies": {"lodash": "^4.17.21"}}
export function chunk(arr: any[], size: number): any[][] 可以使用泛型进行优化 export function chunk<T>(arr: T[], size: number): T[][]
lodash.d.ts 注意声明文件的命名,如果命名为 index.d.ts,那么在 index.ts 中导入 lodash 时依旧是会报错的。
这涉及到 node 中文件的查找逻辑,和模块的查找逻辑类似
./src/index.d.ts、./src/index.ts❎./src/lodash.d.ts、./src/index.ts✅./src/lodash/index.d.ts、./src/index.ts✅
npm run build 先编译,然后运行编译结果 index.js 文件 node ./dist/index.js(在项目的根目录执行该命令)
Object.defineProperty(exports, "__esModule", {value: true});const _ = require("lodash");console.log(_.chunk(["a", "b", "c", "d"], 2));
打印结果为 [ [ 'a', 'b' ], [ 'c', 'd' ] ] 和官方一致。官方 example
🤔 如果直接执行 **npm run dev**,运行 **index.ts**,结果会如何?
答:会报错。

{"compilerOptions": {"target": "es2016","module": "commonjs","lib": ["es2016"],"outDir": "./dist","strictNullChecks": true,// "strictPropertyInitialization": true,"removeComments": true,"noImplicitUseStrict": true,"noImplicitAny": true,"noImplicitThis": true,"experimentalDecorators": true,"emitDecoratorMetadata": true},"include": ["./src"],}
这是由 ts-node 这个库决定的,细节 👉 链接
简述报错原因:无法找到对应的类型声明文件。 ts-node 需要我们告诉它,声明文件的具体位置。因为 ts-node 在默认情况下,不会去读取 tsconfig.json 中的配置:files、include、exclude。所以它无法得知我们所写的声明文件所在的位置。 如果 ts-node 会读 include 配置的内容,就知道声明文件的位置了,自然就不会报错了。从上述代码看来,我们可以得知声明文件是在 include 中的。
在了解到报错原因后,根据 ts-node 官方文档的提示,我们可以通过 compilerOptions.typeRoots 这个配置来告知执行的文件,我们将类型声明文件都放在哪里了。
{"compilerOptions": {"target": "es2016","module": "commonjs","lib": ["es2016"],"outDir": "./dist","strictNullChecks": true,// "strictPropertyInitialization": true,"removeComments": true,"noImplicitUseStrict": true,"noImplicitAny": true,"noImplicitThis": true,"experimentalDecorators": true,"emitDecoratorMetadata": true,"declaration": true,"typeRoots": ["./node_modules/@types","./src/types",]},"include": ["./src"],}
下面介绍三斜线指令。(在测试这个指令时,有报错,所以就按部就班地按照袁老的讲解,记录一下它的用法)
变化前的位置:./src/types/lodash/index.d.ts
变化后的位置:./index.d.ts
根据我们在 tsconfig.json 中的配置,会到 ./src/types 里面查找声明文件。
但是咋们将其位置丢到的根目录下,所以找不到它,但是依旧可以找到 ./src/types 目录下的 global.d.ts 文件。
三斜线指令的作用:在一个声明文件中,包含(引用)另一个声明文件。
在声明文件 ./src/types/global.d.ts 中,引用 ./index.d.ts声明文件,这样就能找到原来的 ./src/types/lodash/index.d.ts 文件了。
/// <reference path="../../index.d.ts" />// declare var console: {// log(message?: any): void;// };// type timeHandler = () => void;// declare function setTimeout(handler: timeHandler, miliseconds: number): number;// declare function setInterval(handler: timeHandler, miliseconds: number): number;
/// <reference path="../../index.d.ts" /> 从当前声明文件的相对位置去查找 ../../index.d.ts 文件。
简述报错原因:无法找到对应的类型声明文件。
ts-node 需要我们告诉它,声明文件的具体位置。因为 ts-node 在默认情况下,不会去读取
tsconfig.json 中的配置:files、include、exclude。所以它无法得知我们所写的声明文件所在的位置。
如果 ts-node 会读 include 配置的内容,就知道声明文件的位置了,自然就不会报错了。从上述代码看来,我们可以得知声明文件是在 include 中的。