搞懂TypeScript的类型声明文件的方方面面

.d.ts 类型声明/定义文件是 TypeScript 重要的一部分, 希望本文可以带你搞懂下面几个问题:

  • 类型声明是怎么被TypeScirpt识别到的?
  • 怎么使用第三方包的类型声明文件?
  • TypeScript代码怎么编译生成类型声明文件?
  • 怎么为第三方包编写类型声明文件,第三方包类型错误怎么进行修正

类型声明文件

TypeScript 中有两种主要的文件类型 .ts.d.ts,以 .d.ts 为后缀的文件,我们称为 TypeScript 的类型声明文件,主要是用来描述模块内所有导出接口的类型信息(声明文件只是对类型进行定义用来做类型检查,不是代码实现,不能进行赋值)。

类型声明是怎么被引用到的

1.TypeScript 内置类型声明

当你安装 TypeScript 时,会自带很多类型声明:lib。其中 es5.full.d.ts 声明文件包含 JavaScript 运行时以及 DOM 中存在各种常见的环境声明。

以 DOM 类型为例:

dart 复制代码
const aEle = document.querySelector('a') // HTMLAnchorElement | null
const canvasEle = document.querySelector('#my_canvas') as HTMLCanvasElement

我们查找a标签,TypeScript 可以自动 推断出类型为 HTMLAnchorElement | null

如果通过元素ID查找,TypeScript无法推断出具体的元素类型,我们可以使用类型断言来明确指定元素类型为 HTMLCanvasElement(关于断言内容可以阅读:搞懂TypeScript中的类型断言和使用场景)。当变量有明确的类型之后,我们也可以等到更好的类型校验和语法提示。

上面说到的 HTMLAnchorElementHTMLCanvasElement 就是 TypeScript 内置的 DOM 类型声明,也就是 <reference lib="dom" /> 的引用内容,具体内容可以参考:dom.generated.d.ts

2.第三方包自带的类型声明

一些第三方包会自带类型声明文件,通过 package.json 文件中的 "types""typings" 字段来指定声明文件位置,以 vue 为例:

json 复制代码
{
  "name": "vue",
  "typings": "types/index.d.ts",
  "exports": {
    ".": {
      "import": {
        "node": "./dist/vue.runtime.mjs",
        "default": "./dist/vue.runtime.esm.js"
      },
      "require": "./dist/vue.runtime.common.js",
      "types": "./types/index.d.ts"
    },
    "./dist/*": "./dist/*",
    "./types/*": "./types/*",
    "./package.json": "./package.json"
  },
  "scripts": {
    "build:types": "rimraf temp && tsc --declaration --emitDeclarationOnly --outDir temp && api-extractor run && api-extractor run -c packages/compiler-sfc/api-extractor.json",
    "test:types": "npm run build:types && tsc -p ./types/tsconfig.json",
    "format": "prettier --write --parser typescript "(src|test|packages|types)/**/*.ts"",
    "ts-check": "tsc -p tsconfig.json --noEmit",
    "ts-check:test": "tsc -p test/tsconfig.json --noEmit",
  }
}

vue 是使用 TypeScript 编写的,在 npm scripts build:types 中通过 tsc 命令来生成类型定义文件到临时文件夹 temp

css 复制代码
tsc --declaration --emitDeclarationOnly --outDir temp

并通过 api-extractortemp 中的多个 .d.ts 进行处理和合并。

注意:package.json 使用 typestypings 来指定类型声明文件,而非 typetype 用来指定文件模块方案,通常为 modulecommonjs(默认)。

3.社区维护的声明文件

一些项目中使用的第三方包可能不是使用 TypeScript 编写的,没办法通过代码直接编译生成 .d.ts 声明文件,也没有在包内维护类型声明文件,我们就可以在 TypeScript 社区查找方案。

Definitely Typed是社区维护的声明类库,一些不是使用TypeScript编写的项目,我们可以找到想要的类型声明,极大的简化了JS项目迁移到TS的难度。安装的 @types/xxx 包,都是维护在 Definitely Typed 中。

比如@types/react就可以在 DefinitelyTyped 中找到。

默认情况下项目中安装的@types/react@types/react-dom 都会被编译器自动引入,TypeScript 会从当前目录向上查找所有 node_modules/@types 中的包,也就是包含 ./node_modules/@types../node_modules/@types 等目录。

但是如果在 tsconfig.json 中通过 typeRoots 配置了指定目录,就会自动引入该目录下的类型声明模块。

perl 复制代码
{
  "compilerOptions": {
    "typeRoots": ["./typings", "node_modules/@types"]
  }
}

注意:typeRoots 默认值为 "node_modules/@types" ,如果指定了其他目录就不会从默认目录中进行查找了,如果还想使用安装的 "@types/xxx" ,需要将默认值也添加进来。

怎么确认使用的第三方是否包含类型定义文件?

在 NPM 上会有对应的标签,使用TypeScript编写,会展示有TS标识:

DefinitelyTyped中维护类型定义,会展示有DT标识:

TypeScript编写的项目,怎么生成类型定义文件

使用TypeScript 开发的项目中可以通过 tsc 来编译生成类型声明文件,在tsconfig.json中配置一下编译选项:

json 复制代码
// tsconfig.json
{
    "compilerOptions": {
        "declaration": true, // 用于指定是否在编译完成后生成相应的*.d.ts文件
        "emitDeclarationOnly": true // 只生成声明文件, 不会生成js文件
    }
}

也可以通过命令行指定参数:

css 复制代码
tsc --declaration --emitDeclarationOnly

和上文介绍的 vue 一样,在 package.json 中通过 "types""typings" 字段来指定类型声明文件即可。

指定编译范围

指定待编译文件有两种方式:

  • 使用 files 属性
  • 使用 includeexclude 属性

如果 filesinclude 都未设置,那么除了 exclude 排除的文件,编译器会默认包含路径下的所有 TS 文件

如果同时设置 filesinclude ,那么编译器会把两者指定的文件都引入。

如果未设置 exclude ,那其默认值为 node_modulesbower_componentsjspm_packages 和编译选项 outDir 指定的路径。

exclude 只对 include 有效,对 files 无效。即 files 指定的文件如果同时被 exclude 排除,那么该文件仍然会被编译器引入。

前面提到,任何被 filesinclude 引入的文件的依赖会被自动引入。

为JS或者第三方代码编写.d.t

在使用TypeScript时,你可能遇到以下问题:

  • 自己使用JS编写的库,不想使用TypeScript重写,为其补充.d.ts
  • 项目中使用的第三方库没有类型声明文件
  • 第三方库缺少某些类型,或者错误定义了某些类型,需要对其进行补充和修正
1. 导入和导出类型定义

如果你想把某个模块相关的类型都定义在一个文件中,可以创建一个.ts 文件,并使用 export 导出,在其他文件中可以直接通过 import 引用。

2. 第三方包类型补充

某些库没有提供类型声明,或者声明类型和实际API不匹配,我们也可以在declare中对其进行补充。

declare可以用来声明全局变量、函数、类或者增强模块的类型:

  • 声明模块 :当使用第三方库时,你可以使用 declare 关键字来声明模块的类型,这样可以避免 TypeScript 报错,也可以有更好的类型检查和自动补全。
typescirpt 复制代码
// global.d.ts
declare module 'my-library-a';

上面的代码中我们声明了my-library-a,但是没有任何类型定义,就会的到隐私的any类型。使用第三方包时,TypeScript总提示类型错误或者缺少类型定义,又不想写类型,就可以使用这种方式。

如果我们只使用到了第三方库的某些方法,或者第三方库的一些类型出现错误,可以使用 declare 进行类型补充

typescript 复制代码
// global.d.ts
declare module 'my-library-b' {  
    export function myFunction(): void;  
    export const myVariable: string;  
}
  • 声明全局变量、函数、对象
typescript 复制代码
// global.d.ts
declare const A: string;  
declare function B(): void;  
declare interface User {  
    name: string;  
    age: number;  
}  
3. 生成.d.ts工具

微软还提供了一个为第三方库编写声明文件工具: dts-gen,使用dts-gen,可以 从任何 JavaScript 对象生成 TypeScript 定义文件 (.d.ts)。

如果你想为自己写的JS代码或者项目中引入的 NPM 模块编写类型声明,可以使用dts-gen,它会进行类型推断自动生成.d.ts文件。

最后,要让编写的类型声明文件(.d.ts)在项目中生效并使用,你需要将其添加到 tsconfig.json 文件的 includefiles 中。

json 复制代码
{  
"compilerOptions": {  
// 其他配置项...  
},  
"include": [  
"src/**/*.ts",  
"typings/*.d.ts" // 假设你的类型声明文件在 typings 文件夹下  
]  
}  

在上述示例中,我们将 typings/*.d.ts 添加到 include 数组中,以确保 TypeScript 编译器会包含 typings 文件夹下的所有 .d.ts 文件。

tsconfig.json配置复用

我们可以使用 extends 来实现配置复用,即一个配置文件可以继承另一个文件的配置属性。

比如,建立一个基础的配置文件 configs/base.json

json 复制代码
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

然后,tsconfig.json 就可以引用这个文件的配置了:

json 复制代码
{
  "extends": "./configs/base",
  "files": [
    "main.ts",
    "supplemental.ts"
  ]
}

这种继承有两种特点:

  • 继承者中的同名配置会覆盖被继承者
  • 所有相对路径都被解析为其所在文件的路径

参考文档

理解 Typescript 配置文件

Definitely Typed

欢迎关注公众号"混沌前端"

相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9152 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風7 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#