MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

TypeScript代码格式化与类型语法兼容方案

2021-09-141.2k 阅读

代码格式化基础:Prettier 入门

在探讨 TypeScript 代码格式化与类型语法兼容方案之前,我们先来了解一款主流的代码格式化工具——Prettier。Prettier 是一个有 opinionated 的代码格式化工具,它能以一种统一的风格格式化代码,减少团队成员之间因代码风格不一致而产生的冲突。

安装与基本使用

  1. 安装:可以通过 npm 或 yarn 进行安装。
    npm install --save-dev prettier
    # 或者使用 yarn
    yarn add --dev prettier
    
  2. 基本使用:安装完成后,可以在命令行中使用 prettier 命令。例如,要格式化一个 src 目录下所有的 TypeScript 文件,可以执行以下命令:
    npx prettier --write src/**/*.ts
    
    --write 选项会直接修改文件,将代码格式化为 Prettier 设定的风格。如果只想检查代码是否符合 Prettier 风格而不进行修改,可以使用 --check 选项:
    npx prettier --check src/**/*.ts
    
    如果代码不符合风格,该命令会返回非零的退出码,常用于持续集成(CI)环境中。

Prettier 配置

Prettier 可以通过多种方式进行配置,最常用的是在项目根目录下创建一个 .prettierrc 文件。这个文件可以是 JSON、YAML 或者 JavaScript 格式。以下是一个简单的 .prettierrc.json 配置示例:

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5"
}
  • semi:是否在语句末尾使用分号,true 表示使用,false 表示不使用。
  • singleQuote:是否使用单引号,true 表示使用单引号,false 表示使用双引号。
  • trailingComma:控制对象、数组等末尾是否添加尾随逗号,es5 表示遵循 ES5 规范,在对象和数组字面量的最后一个元素后添加逗号。

除了 .prettierrc 文件,还可以在 package.json 文件中添加 prettier 字段来进行配置,例如:

{
  "name": "my - typescript - project",
  "version": "1.0.0",
  "prettier": {
    "semi": true,
    "singleQuote": true,
    "trailingComma": "es5"
  }
}

ESLint 与 Prettier 的结合

虽然 Prettier 专注于代码格式化,但在开发过程中,我们还需要进行代码质量检查,这就需要用到 ESLint。ESLint 是一个广泛使用的 JavaScript 和 TypeScript 代码检查工具,它可以帮助我们发现代码中的潜在错误、不良实践等问题。然而,ESLint 和 Prettier 在代码风格的规则上可能存在一些冲突,因此需要进行合理的配置来让它们协同工作。

安装相关依赖

  1. 安装 ESLint
    npm install --save-dev eslint
    # 或者使用 yarn
    yarn add --dev eslint
    
  2. 安装 TypeScript 相关插件
    npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
    # 或者使用 yarn
    yarn add --dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
    
    • @typescript-eslint/parser:这是 ESLint 的解析器,它可以将 TypeScript 代码解析成 ESLint 能够理解的 AST(抽象语法树)。
    • @typescript-eslint/eslint-plugin:这个插件提供了一系列针对 TypeScript 代码的 ESLint 规则。
  3. 安装 ESLint 与 Prettier 集成插件
    npm install --save-dev eslint-plugin-prettier eslint-config-prettier
    # 或者使用 yarn
    yarn add --dev eslint-plugin-prettier eslint-config-prettier
    
    • eslint-plugin-prettier:它会将 Prettier 作为一个 ESLint 规则运行,这样在运行 ESLint 检查时,也会检查代码是否符合 Prettier 风格。如果不符合,会报告错误。
    • eslint-config-prettier:这个配置会关闭 ESLint 中与 Prettier 冲突的规则,避免重复报错。

配置 ESLint

在项目根目录下创建一个 .eslintrc 文件(可以是 JSON、YAML 或者 JavaScript 格式)。以下是一个 .eslintrc.json 的配置示例:

{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint", "prettier"],
  "extends": [
    "plugin:@typescript-eslint/recommended",
    "prettier",
    "plugin:prettier/recommended"
  ],
  "rules": {
    // 在这里可以自定义 ESLint 规则
    "@typescript-eslint/no-unused-vars": "error"
  }
}
  • parser:指定使用 @typescript-eslint/parser 作为解析器。
  • plugins:声明使用 @typescript-eslintprettier 插件。
  • extends
    • "plugin:@typescript-eslint/recommended":这是 @typescript-eslint 插件提供的推荐规则集,包含了一系列针对 TypeScript 代码的最佳实践规则。
    • "prettier":关闭 ESLint 中与 Prettier 冲突的规则。
    • "plugin:prettier/recommended":将 Prettier 作为 ESLint 规则运行,如果代码不符合 Prettier 风格,会报告错误。

在编辑器中集成

  1. Visual Studio Code

    • 安装 ESLint 插件和 Prettier - Code formatter 插件。
    • .vscode/settings.json 文件中添加以下配置:
    {
      "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true
      },
      "eslint.validate": ["typescript", "typescriptreact"]
    }
    

    这样在保存 TypeScript 文件时,ESLint 会自动运行并尝试修复可修复的问题,同时也会确保代码符合 Prettier 风格。

  2. WebStorm

    • 安装 ESLint 和 Prettier 插件。
    • Settings -> Languages & Frameworks -> JavaScript -> Code Quality Tools -> ESLint 中配置 ESLint 相关设置,确保 ESLint 能够正确运行。
    • Settings -> Languages & Frameworks -> JavaScript -> Prettier 中配置 Prettier,启用 Prettier 并设置相关选项。可以通过 File -> Settings -> Tools -> External Tools 配置 Prettier 为外部工具,然后在 File -> Settings -> Tools -> File Watchers 中设置当文件保存时自动运行 Prettier。

TypeScript 类型语法与格式化的特殊考虑

类型注释的格式化

TypeScript 中的类型注释是其重要特性之一,在格式化时需要特别注意。例如,函数参数和返回值的类型注释:

// 未格式化前
function greet(name:string):string{return "Hello, "+name;}
// 格式化后(Prettier 风格)
function greet(name: string): string {
  return "Hello, " + name;
}

Prettier 会在类型注释和变量、函数名之间添加一个空格,使代码更易读。对于复杂的类型,如接口和类型别名,同样要遵循格式化规则:

// 接口定义
interface User {
  name: string;
  age: number;
}
// 类型别名
type Result<T> = {
  data: T;
  success: boolean;
};

在这些定义中,每个属性的冒号后都有一个空格,这是 Prettier 默认的格式化风格。

泛型的格式化

泛型是 TypeScript 强大的特性,在格式化时也有其特点。例如:

// 未格式化前
function identity<T>(arg:T):T{return arg;}
// 格式化后
function identity<T>(arg: T): T {
  return arg;
}

可以看到,泛型参数 T 与函数名和参数之间的空格被正确格式化,使代码更加清晰。当泛型类型比较复杂时,比如多层嵌套的泛型:

function nested<T, U extends Array<T>>(arr: U): T[] {
  return arr.map((item) => item);
}

Prettier 同样能保持良好的格式化,使泛型结构一目了然。

联合类型与交叉类型

  1. 联合类型:联合类型表示一个值可以是多种类型之一。例如:
    let value: string | number;
    value = "hello";
    value = 42;
    
    在格式化时,| 两侧会有空格,增强可读性。
  2. 交叉类型:交叉类型表示一个值必须同时满足多种类型。例如:
    interface A {
      a: string;
    }
    interface B {
      b: number;
    }
    let obj: A & B = { a: "test", b: 123 };
    
    格式化时,& 两侧同样会有空格,使代码结构更清晰。

处理类型语法与格式化的冲突

尽管 Prettier 和 ESLint 能够协同工作,但在 TypeScript 类型语法的某些情况下,仍可能出现冲突。

类型推断与显式类型注释

TypeScript 具有强大的类型推断能力,有时开发者可能会纠结于是否需要显式地添加类型注释。例如:

// 类型推断
let num = 10;
// 显式类型注释
let num2: number = 10;

从代码格式化和风格统一的角度,团队可以制定规则。如果倾向于显式类型注释,ESLint 可以通过规则强制要求。例如,使用 @typescript-eslint/explicit - function - return - type 规则可以强制函数必须有显式的返回类型注释:

{
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "error"
  }
}

这样在代码格式化和风格检查时,就可以确保函数返回类型注释的一致性。

复杂类型结构的换行

在处理复杂的类型结构时,如大型接口或深度嵌套的泛型,换行的处理可能会引起争议。例如:

interface ComplexData {
  prop1: string;
  prop2: number;
  prop3: {
    subProp1: boolean;
    subProp2: {
      nestedProp1: string[];
      nestedProp2: number[][];
    };
  };
}

Prettier 会根据配置的 printWidth(默认 80)来决定是否换行。如果类型结构超出了 printWidth,它会自动换行并进行缩进。但在某些情况下,开发者可能希望手动控制换行以提高代码的可读性。此时,可以通过在代码中适当的位置添加换行符,Prettier 会尊重手动换行,并在其他方面进行格式化。

与自定义类型语法扩展的兼容性

有些团队可能会使用自定义的 TypeScript 类型语法扩展,比如通过装饰器或其他元编程技术。在这种情况下,Prettier 和 ESLint 可能无法直接识别这些扩展。解决办法是编写自定义的解析器插件或规则来处理这些特殊的语法。例如,如果使用了自定义的装饰器语法 @myDecorator,可以编写一个 ESLint 插件来检查装饰器的使用是否符合团队规范,同时确保 Prettier 能够正确格式化包含该装饰器的代码。这可能需要深入了解 ESLint 和 Prettier 的插件开发机制,通过解析和转换 AST 来实现对自定义语法的支持。

自动化流程与持续集成

脚本自动化

为了方便在项目中执行代码格式化和检查,可以在 package.json 中添加一些脚本。例如:

{
  "scripts": {
    "format": "prettier --write src/**/*.ts",
    "lint": "eslint src",
    "check": "prettier --check src/**/*.ts && eslint src"
  }
}
  • format 脚本使用 Prettier 格式化 src 目录下所有的 TypeScript 文件。
  • lint 脚本运行 ESLint 检查 src 目录下的代码。
  • check 脚本先使用 Prettier 检查代码格式,然后运行 ESLint 检查,只有当两者都通过时才返回成功。

这样在开发过程中,开发者可以通过简单的 npm run formatnpm run lintnpm run check 命令来执行相应的操作。

持续集成(CI)集成

在持续集成环境中,如 GitHub Actions、GitLab CI/CD 或 Travis CI,集成代码格式化和检查是非常重要的。以 GitHub Actions 为例,可以在 .github/workflows 目录下创建一个 YAML 文件,例如 ci.yml

name: TypeScript CI
on:
  push:
    branches:
      - main
jobs:
  build:
    runs - on: ubuntu - latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Set up Node.js
        uses: actions/setup - node@v2
        with:
          node - version: '14'
      - name: Install dependencies
        run: npm install
      - name: Format and lint
        run: npm run check

这个工作流会在每次向 main 分支推送代码时触发。它首先检出代码,然后安装 Node.js 和项目依赖,最后运行 npm run check 命令进行代码格式化检查和 ESLint 检查。如果任何一项检查失败,GitHub Actions 会报告错误,阻止代码合并到 main 分支,从而保证了代码库的整体质量和风格一致性。

通过以上全面的方案,我们可以有效地实现 TypeScript 代码格式化与类型语法的兼容,提高代码的可读性、可维护性,并确保团队开发过程中的代码风格统一。无论是在日常开发、自动化流程还是持续集成中,这些方法都能发挥重要作用,助力 TypeScript 项目的稳健发展。