[译]共同配置ESLint、Prettier和Typescript
💡 原文地址
静态分析是一种在不运行代码的情况下审查代码的工具。这与动态分析形成对比:测试等执行您的代码并审查结果的工具。静态分析工具往往存在于速度和功能之间的谱系:
- 格式化工具(例如:Prettier):只快速的格式化你的代码风格,不关心逻辑
- Linters(例如:ESLint):以文件为单位,通过运行一组离散的规则来检查你的代码原始逻辑
- 类型检查(例如:TypeScript):一次生成对所有文件的理解,并验证代码行为是否符合意图
我最近在 React Miami 2023 上做了一个关于为 React 设置 ESLint 和 TypeScript 的演讲,其中包括我的建议。这篇博文涵盖了该演讲中的所有信息:描述如何开始使用JavaScript / TypeScript中的每种形式的静态分析,以及有效使用它们的一些快速提示。
抽象语法树(AST)
在我们深入研究这些工具之前,我想简要提及抽象语法树(AST)。
AST 是一个描述源代码内容的对象。静态分析工具通常会将源代码转换为AST,然后去理解代码。
例如,像 friend = friend || "me"
这样的代码会转换为下面的 AST:
{
"expression": {
"left": "friend",
"operator": "=",
"right": {
"operator": "||",
"type": "LogicalExpression",
"left": "friend",
"right": "\"me\""
}
},
"type": "AssignmentExpression"
}
如果你想知道 TypeScript ASTs 是如何工作的,你可以在 ASTs and typescript-eslint 上找到相关内容,并在 typescript-eslint.io/play 上进行验证
AST 的概念有时候会出现在一些工具的文档中。虽然你无需了解AST即可使用静态分析工具,但他们通常是一个有用的概念。只要知道,当有人说AST时,他们谈论的是工具如何表示你的代码。
理论知识到此为止!接下来让我们深入研究这些工具。
格式化
格式化工具通过读取你的源代码,忽略格式,并建议你如果编写它。例如,给定一下格式奇怪的代码:
friend = friend
|| "me"
格式化工具可能会像这样去重写它:
friend = friend|| "me"
请注意这不会更改代码的逻辑,它只会让代码更容易被人阅读。这太棒了,使用格式化工具,我们不需要手动格式化文件。
Prettier 是如今最常见的格式化工具。你可以通过将其作为依赖项来开始使用,然后在当前目录.
运行 --write
来自动格式化你的所有文件:
npm install prettier -D
npx prettier . --write
我鼓励你阅读 Prettier 文档和 Prettier 安装指南来获取更多的细节和配置。
编辑器格式化设置
我还鼓励你在 VS Code 工作区中启用 Prettier 拓展:
// .vscode/extensions.json
{
"recommendations": ["esbenp.prettier-vscode"]
}
然后将其设置为默认格式化工具,在保存时自动格式化:
// .vscode/settings.json
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
这样,每次你保存文件或运行 VS Code 格式化文档命令时, Prettier 将会为你完全格式化文档。这意味你不需要手动的修复空格、换行符等。
Prettier 也有配置选项。但是我通常都是使用默认配置。只要我的格式是一致的,我就不会陷入纠结。我通常只会在配置中更改一个选项:
// .prettierrc.json
{
"useTabs": true
}
如果你想更多的定制你的格式,你可能更喜欢使用 dprint 作为格式化工具,它比 Prettier 更具可配置型,尽管使用较少。
Linting
linter 是对源码运行一组检查的工具。现代的 linters,比如:ESLint、JavaScript标准的 linter,通常将它们设置为离散规则(规则独立运行,并且无法查看启用了哪些其他规则)。每个规则都可能报告它不喜欢的代码,并且每个消息可能包含可选的自动修复。
例如,如果你启用了 ESLint 的 logical-assignment-operators
规则,则会收到一条消息和建议的修复方法分,如下所示:
- friend = friend || "me"
+ friend ||= "me";
Assignment (=) can be replaced with operator assignment (||=).
要在本地开始使用 ESLint,你可以将其作为依赖项安装,运行其初始化程序来创建一个配置,并在当前目录.
运行 ESLint:
npm install eslint -D
npm init @eslint/config
npx eslint .
ESLint 配置
你的 ESLint 配置文件是所有 ESLint 插件(添加其他规则或其他 linting 行为的npm 包)和要启用或禁用的规则的配置选项的描述。每个规则都可以设置为以下三个值之一:
"off"
:根本不会运行"warn"
:消息作为警告(黄色波浪线)显示,不会导致 ESLint 以非0状态码退出(即不会构建失败)"error"
:消息作为错误(红色波浪线)显示,并应导致 ESLint 以非0状态码退出(即构建失败)
手动配置每个规则是一项繁重的工作,许多项目启用了数百个规则!相反,ESLint 允许从预设配置中继承配置,我强烈建议至少从 ESLint 的 eslint:recommended
配置进行拓展,该配置包含 ESLint 团队发现对绝大多数 JavaScript 项目有用的规则
// .eslintrc.js
module.exports = {
extends: "eslint:recommended",
};
精细规则配置
你始终可以禁用对你无用或者有太多错误的规则。没错,禁用 lint 规则是可以的!linter 跟其他工具一样,应该根据你的需求进行配置。
我对配置 ESLint 的一般建议是:
-
安装与你项目相关的所有插件
-
从每个插件的的推荐配置进行拓展
-
在 ESLint 配置中:
- 禁用你知道你不需要的任何规则,并解释原因
- 还要禁用你想要但是还没有时间启用的规则,并提交 issuce 来最终启用
// .eslintrc.js
module.exports = {
extends: "eslint:recommended",
rules: {
// These rules are enabled by default, but we don't want
"some-annoying-rule": "off", // (conflicts with XYZ preference)
// Todo: these rules might be useful; we should investigate each
"powerful-rule": "off", // (#123)
},
};
为 React 配置 ESLint
由于链接的演讲是在 React 会议上给出的,它还展示了为 React 配置 ESLint。任何使用 JSX 和纯 JavaScript 的项目都需要将 parserOptions.ecmaFeatures.jsx
设置为 true
,以便 ESLint 的解析器知道允许 JSX。React 有两个常用的插件: eslint-plugin-react
用于所有 React, eslint-plugin-react-hooks
专门用于 hooks。 eslint-plugin-react
还要求配置 settings.react.version
,以便它知道要使用哪个特定于 React 版本的行为运行。
// .eslintrc.js
module.exports = {
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
plugins: ["react", "react-hooks"],
settings: {
react: {
version: "detect",
},
},
};
其他框架/库有自己的插件,包括 eslint-plugin-astro
、 eslint-plugin-solid
等。****
更多 Linting 标志
我通常建议在 ESLint 运行时再启用两个标志。
报告未使用的禁用指令
禁用指令是代码中的注释,用于禁用特定代码区域的部分或全部 ESLint 规则,例如,禁用单行日志的no-console
:
// eslint-disable-next-line no-console
console.log("Hello, world!");
默认情况下,ESLint 不会警告你,如果你把这些评论留在不需要它们的地方:
// eslint-disable-next-line no-console
myFancyLogger("Hello, world!");
--report-unused-disable-directives
导致 ESLint 将不必要的禁用指令视为与实际 lint 规则的消息相同。
// package.json
{
"scripts": {
"lint": "eslint . --report-unused-disable-directives"
}
}
最大警告数
将值设置为 "warn"
的规则不会导致 ESLint 生成失败。根据我的经验,将规则保留为警告而不是错误会使它们随着时间的推移而累积,这会训练用户忽略它们。我建议仅对新启用的规则暂时使用 "warn"
,并在可能的情况下将所有规则配置为 "error"
。
--max-warnings
允许指定允许的最大警告数。如果 ESLint 收到的警告级别规则投诉多于该数字,它将切换到现有规则,并显示错误代码。
我建议将该数字保持在尽可能低的水平,最好是 0
:
// package.json
{
"scripts": {
"lint": "eslint . --max-warnings 0"
}
}
编辑器 Linting 设置
我鼓励你在 VS Code 的工作区中启用 ESLint 拓展:
// .vscode/extensions.json
{
"recommendations": ["dbaeumer.vscode-eslint"]
}
然后将其设置为默认格式化工具,在保存时自动运行:
// .vscode/settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
这样,每次保存文件或运行 VS Code ESLint: 修复所有可自动修复的问题命令时,ESLint 都会为您 --fix
任何可修复的问题。如果您使用像 eslint-plugin-simple-import-sort
这样的插件,这将非常有用(我强烈推荐!)。
停止使用 ESLint 进行格式化
格式化工具不是 linter!linter 也不是格式化工具!这两种工具的内部工作原理不一样。
- 格式化工具一次性重新格式化所有文件,这意味着比 linter 更快,但是不会查找逻辑问题
- linter 运行一组离散的规则,这意味着比格式化工具慢,但是可以找到逻辑问题并提供修复建议
尽管 linter 可以具有针对格式量身定制的规则(例如 indent
, max-len
, semi
),这些规则变得非常复杂且难以维护,因为它们需要处理所有边缘情况。在 typescript-eslint 领域,我们已经完全放弃了 indent
规则。格式化规则是邪恶的,浪费时间进行维护,并且不是格式化代码的正确方法。请使用专用的格式化工具!
类型检查
今天,TypeScript 是 JavaScript 的标准类型检查器。人们喜欢将 TypeScript 描述为“JavaScript 的超集”(又名“JavaScript with types”)。但“TypeScript”这个词实际上指的是TypeScript团队提供的四件事:
- 编程语言:一种语言的描述,其语法包括 JavaScript 中的所有语法和一些特定于类型的新内容
- 类型检查器:一个用于读取文件并报告代码意图和执行方式不匹配的程序
- 编译器:运行类型检查器的程序,以及将 TypeScript 源码转换为等效的 JavaScript 代码
- 语言服务:在编辑器/IDE(如:VS Code)中运行编辑器和类型检查的程序
TypeScript 很有用,因为 JavaScript 中没有标准的内置方法来描述代码背后的意图。例如,这个 JavaScript 代码段声明了一个变量,但从不解释它允许包含什么类型的值:
let myValue; // what is this?!
TypeScript 类型注释语法将允许描述其意图(允许存储的值类型):
let myValue: number;
并且 TypeScript 类型检查器可以警告我们,如果我们为该变量分配了与我们声明的意图不符的内容:
myValue = "not a number";
// Error: Type 'string' is not assignable to type 'number'.
要在本地开始使用 TypeScript,您可以将其做为依赖项安装,运行其 init 命令以创建启动器配置,然后运行它:
npm install typescript -D
npx tsc --init
npx tsc
TypeScript 配置
tsc --init
将为您提供一个良好的启动配置。以下配置是我为大多数使用 React 或其他使用 JSX 的框架的项目推荐的最小配置:
"jsx"
:告诉 TypeScript 应运行 jsx 语法"module"
:TypeScript 应该假设代码在哪个模块运行"strict"
:启用一套有用的类型检查规则,使 TypeScript 更加严格(因此它会捕获更多的问题)"target"
:指示 TypeScript 应假定哪些全局API 和环境语法功能可用
一些编译器宣讲在大部分项目中都会起作用:
"esModuleInterop"
:告诉 TypeScript 在 CommonJS 和 ESM 模块之间导入时不要那么挑剔(因为默认情况下它非常迂腐)"forceConsistentCasingInFileNames"
:启用 TypeScript,让您知道是否使用错误的大小写输入"skipLibCheck"
: 防止 TypeScript 花费时间进行类型检查node_modules
aka.ms/tsconfig 介绍了每个可用的编译器选项。我还在学习打字稿>第13章:配置选项中更深入地解释了其中的许多。
请注意,编译器选项通常由框架启动器(如 create-next-app
)为您设置。只要他们设置 strict: true
,你应该没问题。****
TypeScript 编辑器配置
我通常建议在VS Code 工作区中使用如下设置:
// .vscode/settings.json
{
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
"typescript.tsdk": "node_modules/typescript/lib"
}
"eslint.rules.customizations"
告诉 VS Code 将所有 ESLint 规则投诉可视化为黄色(警告)波浪线,而不是与其配置的严重性匹配的波浪线颜色。我发现这很有用,因为 TypeScript 投诉通常显示为红色(错误)波浪线。让两个工具以相同的颜色显示投诉可能会令人困惑。以红色显示 TypeScript 投诉,以黄色显示 ESLint 投诉有助于人们了解哪个是哪个。
"typescript.tsdk"
告诉 VS Code 它应该使用项目已安装的 TypeScript 包作为 IDE 工具,而不是计算机的全局 VS Code / TypeScript 安装。这很好,因为项目安装的 TypeScript 版本可能与 VS Code 不同。您不希望在终端上运行 tsc
与从 VS Code 的语言服务运行 TypeScript 的结果可能不同。
TypeScript 不是 Linter
不要将 linting 和类型检查混淆。两者是不一样的。
- 传统的 linter 运行一组离散可配置的规则,一次只能看到一个文件,这意味着它比类型检查器更快、更可配置。
- 类型检查器可以完全理解所有文件,这意味着它比传统的 linter 慢,但在推断方面更强大。
您可以将区别视为:
- 类型检查器让您知道代码何时明显没有意义(例如,在应该只接收字符串的地方提供数值)
- Linters 会让您知道代码何时有意义,但可能是错误的(例如,调用标记为
@deprecated
的函数)
我之所以说传统的 linter,是因为稍后我们将看到如何启用使用 TypeScript API 的强大 lint 规则。 ****
Linting TypeScript 代码
默认情况下,ESLint 只理解 JavaScript 语法,而不能理解 TypeScript 中包含的新语法。它的核心规则不适用于特定于 TypeScript 的逻辑或最佳实践。这就是为什么 Typescript-eslint 为 ESLint 用户提供了两个软件包:
@typescript-eslint/eslint-plugin
:提供针对 TypeScript 代码量身定制的 lint 规则和预设配置- •
@typescript-eslint/parser
: 告诉 ESLint 如何读取 TypeScript 语法
Prettier也在内部使用 typescript-eslint,这就是它支持 TypeScript 语法的方式。
typescript-eslint.io 包括一个用于检查 TypeScript 代码的入门指南。我可以建议使用的最直接的 linter 配置利用这两个包来扩展 ESLint 推荐的规则以及 [plugin:@typescript-eslint/recommended](<https://typescript-eslint.io/linting/configs#recommended>)
,这是 TypeScript 的等效入门配置:
// eslintrc.js
module.exports = {
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
};
例如, @typescript-eslint/prefer-as-const
规则可以通知开发人员使用 as const
而不是在类型断言中重新键入文本值:
- let me = { name: "ReactMiami" as "ReactMiami" };
+ let me = { name: "ReactMiami" };
类型感知规则
传统的 lint 规则一次只能看到一个文件。typescript-eslint 提供的 API 允许规则进入 TypeScript 的类型检查器 - 从而对更强大的 lint 规则进行分类。这些“类型感知”lint 规则未在 plugin:@typescript-eslint/recommended
中启用,因为它们比传统的 lint 规则慢得多,因为它们以类型检查的速度运行(需要在整个项目上运行)。
typescript-eslint的类型信息代码检查指南展示了启用这些规则所需的步骤。最小要求如下:
module.exports = {
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
+ "plugin:@typescript-eslint/recommended-requiring-type-checking",
],
plugins: ["@typescript-eslint"],
parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
};
plugin:@typescript-eslint/recommended-requiring-type-checking
是我们推荐的配置,它另外启用类型感知规则- typescript-eslint 需要
parserOptions.project
才能知道哪个 TSConfig 包含用于生成类型信息的编译器选项;true
指示使用最接近每个源文件的文件
例如, @typescript-eslint/await-thenable
报告在非 thenable 语句(如 Promise
)上使用的任何 await
。
据我所知,使用 typescript-eslint 的类型感知 lint 规则是当今JavaScript / TypeScript项目可以获得的最强大的lint规则。我强烈建议使用它们!****
将一切整合到一起
让我们回顾一下这些工具是如何交互的:
- 格式化工具(如 Prettier)可快速格式化代码,无需担心代码逻辑
- 诸如 ESLint 之类的 Linter 在代码逻辑上运行一组离散规则
- 类型检查器(如 TypeScript)可以了解您的项目,并在违反声明的意图时出错
- typescript-eslint 允许 ESLint 解析 TypeScript 代码,并将 TypeScript 类型检查 API 公开给 ESLint 规则
Linting TypeScript in 2023 :是一个演示存储库,显示了所有这些工具的配置,以及使用三个类型检查的打字稿-eslint 规则在 React 应用程序中捕获三个错误的示例。
另请参阅单独的常见问题解答文章,了解有关静态分析的各种问题。****
转载自:https://juejin.cn/post/7238027797313290277