熬丁 發表於 2022-12-29 10:13:00

TypeScript 前端工程最佳实践

<h1 id="作者王春雨">作者:王春雨</h1>
<h1 id="前言">前言</h1>
<p>随着前端工程化的快速发展, TypeScript 变得越来越受欢迎,它已经成为前端开发人员必备技能。 TypeScript 最初是由微软开发并开源的一种编程语言,自2012年10月发布首个公开版本以来,它已得到了人们的广泛认可。TypeScript 发展至今,已经成为很多大型项目的标配,其提供的静态类型系统,大大增强了代码的可读性、可维护性和代码质量。同时,它提供最新的JavaScript特性,能让我们构建更加健壮的组件,新版本不断迭代更新,编写前端代码也越来越香。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e8e40458145d47a48f42979180ad10a5~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p>typescript 下载量变化趋势(来自于 npm trends)</p>
<h1 id="1-为什么使用-typescript">1 为什么使用 TypeScript</h1>
<p>微软提出 TypeScript 主要是为了实现两个目标:为 JavaScript 提供可选的类型系统,兼容当前及未来的 JavaScript 特性。首先类型系统能够提高代码的质量和可维护性,国内外大型团队经过不断实践后得出一些结论:</p>
<ul>
<li>类型有利于代码的重构,它有利于编译器在编译时而不是运行时发现错误;</li>
<li>类型是出色的文档形式之一,良好的函数声明胜过冗长的代码注释,通过声明即可知道具体的实现;</li>
</ul>
<p>像其他语言都有类型的存在,如果强加于 JavaScript 之上,类型可能会有一些不必要的复杂性,而 TypeScript 在两者之间做了折中处理尽可能地降低了入门门槛,它使 JavaScript 即 TypeScript ,为 JavaScript 提供了编译时的类型安全。TypeScript 类型完全是可选的,原来的 .js 文件可以直接被重命名为 .ts ,ts 文件可以被编译成标准的 JavaScript 代码,并保证编译后的代码全部兼容,它也被成为 JavaScript 的 “超集”。没有类型的 JavaScript 语法虽然简单灵活,使用的变量是弱类型,但是比较难以掌握,TypeScript 提供的静态类型检查,很好的弥补了 JavaScript 的不足。</p>
<p>TypeScript 类型可以是隐式的也可以是显式的,它会尽可能安全地推断类型,以便在代码开发过程中以极小的成本为你提供类型安全,也可以使用显式的声明类型注解让编译器编译出我们想要的内容,更重要的是为下一个必须阅读代码的开发人员理解代码逻辑。</p>
<p>类型错误也不会阻止JavaScript 的正常运行,为了方便把 JavaScript 代码迁移到 TypeScript,即使存在编译错误,TypeScript 也会被编译出完整的 JavaScript 代码,这与其他语言的编译器工作方式有很大不同,这也正是 TypeScript 被青睐的另一个原因。</p>
<p><strong>TypeScript 的特点还有很多比如下面这些:</strong></p>
<ol>
<li>免费开源,使用 Apache 授权协议;</li>
<li>基于ECMAScript 标准进行拓展,是 JavaScript 的超集;</li>
<li>添加了可选静态类型、类和模块;</li>
<li>可以编译为可读的、符合ECMAScript 规范的 JavaScript;</li>
<li>成为一款跨平台的工具,支持所有的浏览器、主机和操作系统;</li>
<li>保证可以与 JavaScript 代码一起使用,无须修改(这一点保证了 JavaScript 项目可以向 TypeScript 平滑迁移);</li>
<li>文件扩展名是 ts/tsx;</li>
<li>编译时检查,不污染运行时;</li>
</ol>
<p>总的来说我们没有理由不使用 TypeScript, 因为 JavaScript 就是 TypeScript,TypeScript 可以让 JavaScript 更美好。</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/74900537d73744f7a48bfb41e7833297~tplv-k3u1fbpfcp-zoom-1.image"></p>
<h1 id="2-开始使用-typescript">2 开始使用 TypeScript</h1>
<h2 id="21-安装-typescript-依赖环境">2.1 安装 TypeScript 依赖环境</h2>
<p>TypeScript 开发环境搭建非常简单,大部分前端工程都集成了 TypeScript 只需安装依赖增加配置即可。所有前端项目都离不开 NodeJS 和 npm 工具,npm 命令安装 TypeScript,通常TypeScript 自带的 tsc 并不能直接运行TypeScript 代码,因此我们还会安装 TypeScript 的运行时 ts-node:</p>
<pre><code>npm install --save-dev typescript ts-node
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab8c25cb0ebe491fb9e38e6addff5ea4~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p></p>
<p>Babel 与 TypeScript 结合的关键依赖 @babel/preset-typescript,它提供了从 TypeScript 代码中移除类型相关代码(如,类型注解,接口,类型文件等),并在 babel.config.js 文件添加配置选项:</p>
<pre><code>npm install -D @babel/preset-typescript

// babel.config.js
{
"presets": [
// ...
"@babel/preset-typescript"
]
}
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f200c6a3b550432b8927e3a1ba937913~tplv-k3u1fbpfcp-zoom-1.image"></p>
<h1 id="22-配置-typescript">2.2 配置 TypeScript</h1>
<p>TypeScript 本身提供了只使用参数在命令行编译 TypeScript 文件,但是在实际项目开发时我们都会使用 tsconfig.json ,如果项目中没有此文件,可以手动创建也可以使用命令行创建(tsc —init)。使用 TypeScript 初期仅需要一份默认的 tsconfig.json 即可,它包含了一下基本的编译选项相关信息,当我们需要定制编译选项时就需要去了解每一项具体的含义,编译选项解读如下:</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61e04eff49724679afb4e18208dcd96e~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p>2.严格的类型检查选项:</p>
<ul>
<li>strict: 是否启用严格类型检查选项,可选 ture | false</li>
<li>allowUnreachableCode: 是否允许不可达的代码出现,可选 ture | false</li>
<li>allowUnusedLabels: 是否报告未使用的标签错误,可选 ture | false</li>
<li>noImplicitAny: 当在表达式和声明上有隐式的 any 时是否报错,可选 ture | false</li>
<li>strictNullChecks: 是否启用严格的 null 检查,可选 ture | false</li>
<li>noImplicitThis: 当 this 表达式的值为 any 时,生成一个错误,可选 ture | false</li>
<li>alwaysStrict: 是否以严格模式检查每个模块,并在每个文件里加入 use strict,可选 ture | false</li>
<li>noImplicitReturns: 当函数有的分支没有返回值时是否会报错,可选 ture | false</li>
<li>noFallthroughCasesInSwitch: 表示是否报告 switch 语句的 case 分支落空(fallthrough)错误;</li>
</ul>
<p>3.模块解析选项:</p>
<ul>
<li>moduleResolution: 模块解析策略默认为 node 比较通用的一种方式基</li>
<li>commonjs 模块标准,另一种是 classic 适用于其他 module 标准,如 amd、 umd、 esnext 等等</li>
<li>baseUrl: “./“ 用于解析非相对模块名称的根目录</li>
<li>paths: 模块名到基于 baseUrl 的路径映射的列表,格式 {}</li>
<li>rootDirs: 根文件夹列表,其做好内容表示项目运行时的结果内容,格式 []</li>
<li>typeRoots: 包含类型声明的文件列表,格式 [“./types”] ,相对于配置文件的路径解析;</li>
<li>allowSyntheticDefaultImports: 是否允许从没有设置默认导出的模块中默认导入</li>
</ul>
<p>4.Source Map 选项:</p>
<ul>
<li>sourceRoot: ./ 指定调试器应该找到 TypeScript 文件而不是源文件的位置</li>
<li>mapRoot: ./ 指定调试器应该找到映射文件而不是生成文件的位置</li>
<li>inlineSourceMap: 是否生成单个 sourceMap 文件,不是将 sourceMap 生成不同的文件</li>
<li>inlineSources: 是否将代码与 sourceMap 生成到一个文件中,要求同时设置 inlineSourceMap 和 sourceMap 属性</li>
</ul>
<p>5.其它选项:</p>
<ul>
<li>experimentalDecorators: 是否启用装饰器</li>
<li>emitDecoratorMetadata: 是否为装饰器提供元数据的支持</li>
</ul>
<p>6.还可以使用include 和 exclude 选项来指定编译器需要和不需要编译的文件,一般增加必要的 exclude 文件会提升编译性能:</p>
<pre><code> "exclude": [
    "node_modules",
    "dist"
...
],
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b4cffb02fa84e8796ab76528cfbf258~tplv-k3u1fbpfcp-zoom-1.image"><br>
图中蓝色的为基本类型,红色为 TypeScript 支持的特殊类型</p>
<p>TypeScript 的类型注解相当于其它语言的类型声明,可以使用 let 和 const 声明一个变量,语法如下:</p>
<pre><code>// let 或 const 变量名:数据类型 = 初始值;
//例如:
let varName: string = 'hello typescript'
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/071c884b6a454812b968a376109f80ff~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p>泛型类型:是指参数化一个或多个类型的泛型函数、类、接口等。泛型类型允许我们编写能够支持不同类型的通用代码,从而实现高度的代码重用。使用泛型让代码的组件化程度更高,我们可以把这些泛型组件用作基本模块,通过组合它们实现期望的行为,同时在组件之间只保留下最小限度的依赖。</p>
<h2 id="32-泛型数据结构">3.2 泛型数据结构</h2>
<p>假如我们要实现一个数值二叉树和字符串链表。把二叉树实现为一个或多个结点,每个结点存储一个数值,并引用其左侧和右侧的子结点,这些引用指向结点,如果没有子结点,可以指向 undefined。</p>
<pre><code>class NumberBinaryTreeNode {
value: number
left: NumberBinaryTreeNode | undefined
right: NumberBinaryTreeNode | undefined

constructor(value: number) {
    this.value = value
}
}
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6959028da6ae4aa386e5787e4a2bfaea~tplv-k3u1fbpfcp-zoom-1.image"></p>
<h1 id="5-typescript-内置工具类型">5 TypeScript 内置工具类型</h1>
<p>TypeScript 提供了很多内置的工具类型根据不同的应用场景选择合适的工具可以减轻很多工作,减少冗余代码提升代码质量,下面列举了一些常用的工具:</p>
<ul>
<li>Partial:构造一个新类型,并将类型 T 的所有属性变为可选属性;</li>
<li>Required:构造一个新类型,并将类型 T 的所有属性变为必选属性;</li>
<li>Readonly: 构造一个新类型,并将类型 T 的所有属性变为只读属性;</li>
<li>Pick: 已有对象类型中选取给定的属性名,返回一个新的对象类型;</li>
<li>Omit: 从已有对象类型中剔除给定的属性名,返回一个新的对象类型;<br>
示例代码:</li>
</ul>
<pre><code>interface A {
x: number
y: number
z?: string
}
type T0 = Partial&lt;A&gt;
// 等价于
type T0 = {
    x?: number | undefined;
    y?: number | undefined;
    z?: string | undefined;
}

type T1 = Required&lt;A&gt;
// 等价于
type T1 = {
    x: number;
    y: number;
    z: string;
}

type T2 = Readonly&lt;A&gt;
// 等价于
type T2 = {
    readonly x: number;
    readonly y: number;
    readonly z?: string | undefined;
}

type T3 = Pick&lt;A, 'x'&gt;
// 等价于
type T3 = {
    x: number;
}

type T4 = Omit&lt;A, 'x'&gt;
// 等价于
type T4 = {
    y: number;
    z?: string | undefined;
}
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2a76b317aa2349d39afc20faa61c7bf7~tplv-k3u1fbpfcp-zoom-1.image"></p>
<ul>
<li>左侧编写 TS 代码,右侧自动生成编译后的代码;</li>
<li>可以自主选择 TypeScript 编译版本;</li>
<li>版本列表最后一项是一个特殊版本 “Nightly” 即 “每日构建版本”,想尝试最新功能可以试试;</li>
<li>支持 TypeScript 大部分配置项和编译选项,可以模拟本地环境,查看代码片段的输出结果;</li>
</ul>
<h2 id="62-jsdoc-generator-插件">6.2 JSDoc Generator 插件</h2>
<p>如果使用的是 vscode 编辑器直接搜索( JSDoc Generator 插件)插件地址:https://marketplace.visualstudio.com/items?itemName=crystal-spider.jsdoc-generator 安装成功后,使用 Ctrl + Shift + P 打开命令面板,可以进行如下操作可以自动生成带有 TypeScript 声明类型的文档注释:</p>
<ul>
<li>
<p>选择 Generate JSDoc 为当前光标处代码生成文档注释;</p>
</li>
<li>
<p>选择Generate JSDoc for the current file 为当前文件生成文档注释;</p>
<p>6.3 代码格式化工具<br>
VSCode 仅提供了基本的格式化功能,如果需要定制更加详细的格式化规则可以安装专用的插件来实现。我们使用 Prettier 功能非常强大(推荐使用),它是目前最流行的格式化工具: https://prettier.io/,同时也提供了一个在线编辑器:https://prettier.io/playground/<strong>6.4 模块导入自动归类和排序</strong>在多人协作开发时代码越来越复杂,一个文件需要导入很多模块,每个人都会加加着加着就有点乱了,绝对路径的、相对路径的,自定义模块、公用模块顺序和类别都是混乱的,模块导入过多还会出现重复的。引入 TypeScript 之后检查更加严格,导入的不规范会有错误提示,如果只靠手动优化工作量大且容易出错。VSCode 编辑器提供了按字母顺序自动排序和归类导入语句的功能,直接按下快捷键“Shift + Alt + O”即可优化。也可以通过右键菜单“Source Action” 下的 “Organize Imports” 选项来进行优化导入语句。<strong>6.5 启用 CodeLens</strong></p>
</li>
</ul>
<p>CodeLens 是一项特别好用的功能,它能够在代码的位置显示一些可操作项,例如:</p>
<ul>
<li>显示函数、类、方法和接口等被引用的次数以及被哪些代码引用;</li>
<li>显示接口被实现的次数以及谁实现了该接口;</li>
</ul>
<p>VSCode 已经内置了 CodeLens 功能,只需要在设置面板开启,找到TypeScript 对应的 Code Lens 两个相关选项并勾选上:</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ed06d089c4924555972ef4e4fe5afdf6~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p>开启后的效果,出现引用次数,点击 references 位置可以查看哪里引用了:</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50785a6a276e40bd92f1dcbdc129dbd1~tplv-k3u1fbpfcp-zoom-1.image"></p>
<h2 id="66-接口自动生成-typescript-类型">6.6 接口自动生成 TypeScript 类型</h2>
<p>对于前端业务开发来说,最频繁的工作之一就是和接口打交道,前端和接口之间经常出现出入参不一致的情况,后端的接口定义也需要在前端定义相同的类型,大量的类型定义如果都靠手写不仅工作量大而且容易出错。因此,我们需要能够自动生成这些接口类型定义的 TypeScript 代码。VSCode 插件市场就有这样一款插件——Paste JSON as Code 。<br>
插件地址:https://marketplace.visualstudio.com/items?itemName=quicktype.quicktype<br>
安装这个 VSCode 插件可以将接口返回的数据,自动转换成类型定义接口文件。<br>
1.剪贴板转换成类型定义:首先将 JSON 串复制到剪贴板, Ctrl + Shift + P 找到命令:Paste JSON to Types -&gt; 输入接口名称</p>
<pre><code>{"a":1,"b":"2","c":3} // 复制这段 JSON 代码

// Generated by https://quicktype.io
export interface Obj {
a: number;
b: string;
c: number;
}
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/727ad48b451b49598ac49749531a8afc~tplv-k3u1fbpfcp-zoom-1.image"></p>
<p>对应大量且冗长的接口字段一键生成是不是很方便呢!希望这些工具能给每一位研发带来帮助提升研发效率。</p>
<h1 id="7-总结">7 总结</h1>
<p>TypeScript 是一个比较复杂的类型系统,本文只是对其基本用法进行了简要说明和工作中用到的知识点,适合刚开始使用 TypeScript 或者准备使用的研发人员,对于更深层次的架构设计和技术原理并未提及,如果感兴趣的可以线下交流。用好 TypeScript 可以编写出更好、更安全的代码希望对读到本文的有所帮助并能在实际工作中运用。希望本文作为 TypeScript 入门级为读者做一个良好的开端。感谢阅读!!</p>
<p>​</p><br><br>
来源:https://www.cnblogs.com/Jcloud/p/16997752.html
頁: [1]
查看完整版本: TypeScript 前端工程最佳实践