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

TypeScript 开发环境配置:tsconfig.json 文件详解

2022-11-063.5k 阅读

一、tsconfig.json 文件的基础介绍

在前端开发使用 TypeScript 时,tsconfig.json 文件起着至关重要的作用。它是 TypeScript 项目的配置文件,通过对该文件的设置,可以控制 TypeScript 编译器(tsc)如何将 TypeScript 代码编译为 JavaScript 代码。

简单来说,tsconfig.json 文件就像是一份指令清单,告诉编译器在编译过程中需要遵循哪些规则,包括哪些文件需要编译,编译目标是什么,是否启用严格模式等等。当在项目根目录下存在 tsconfig.json 文件时,在该目录及子目录下执行 tsc 命令,编译器就会根据这个配置文件的设置进行编译操作。

例如,一个最基础的 tsconfig.json 文件可能看起来像这样:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs"
    }
}

上述配置中,compilerOptions 是配置选项的集合。target 设置为 es5 表示将 TypeScript 代码编译为 ES5 版本的 JavaScript 代码,这是一个较为广泛兼容的版本。module 设置为 commonjs 表示使用 CommonJS 模块系统,这在 Node.js 环境中是常用的模块规范。

二、compilerOptions 常用配置项详解

(一)目标设置

  1. target 这个选项指定了编译后的 JavaScript 代码的目标 ECMAScript 版本。常见的值有 es3es5es6(或 es2015)、es2016es2017es2018es2019es2020es2021es2022 以及 esnext。 例如,如果你的项目需要兼容较老的浏览器,可能会选择 es5
{
    "compilerOptions": {
        "target": "es5"
    }
}

而如果项目是面向现代浏览器或 Node.js 环境,且不需要考虑旧版本兼容性,可以选择 es2020 等较新的版本,以利用新的语言特性:

{
    "compilerOptions": {
        "target": "es2020"
    }
}
  1. module module 选项用于指定生成的 JavaScript 代码所使用的模块系统。常见的值包括 nonecommonjsamdsystemumdes6(或 es2015)、es2020esnext
  • commonjs:这是 Node.js 使用的模块系统,适合在 Node.js 环境中开发的项目。
{
    "compilerOptions": {
        "module": "commonjs"
    }
}
  • es6(或 es2015):这是 ECMAScript 6 引入的原生模块系统,在现代浏览器和一些支持 ES6 模块的运行环境中使用。
{
    "compilerOptions": {
        "module": "es2015"
    }
}
  • umd:通用模块定义(Universal Module Definition),可以在多种环境(如浏览器和 Node.js)中使用,它会生成兼容多种模块加载器的代码。
{
    "compilerOptions": {
        "module": "umd"
    }
}

(二)严格模式相关

  1. strict 这是一个非常重要的布尔选项。当设置为 true 时,它会启用一系列严格的类型检查规则,能帮助开发者在编译阶段发现更多的类型错误,提高代码的健壮性。启用 strict 相当于同时启用了 noImplicitAnystrictNullChecksstrictFunctionTypesstrictBindCallApplystrictPropertyInitializationnoFallthroughCasesInSwitch 等多个选项。
{
    "compilerOptions": {
        "strict": true
    }
}
  1. noImplicitAny 当该选项为 true 时,如果一个变量或函数参数没有显式指定类型,TypeScript 编译器不会默认将其推断为 any 类型,而是会报错。这有助于避免在代码中不经意间使用 any 类型,从而失去类型检查的优势。 例如,在下面的代码中,如果 noImplicitAnytrue,这段代码会报错:
function greet(name) {
    return `Hello, ${name}`;
}

因为 name 参数没有指定类型。正确的写法应该是:

function greet(name: string) {
    return `Hello, ${name}`;
}
  1. strictNullChecks 开启这个选项后,TypeScript 会对 nullundefined 进行更严格的类型检查。在未开启时,nullundefined 可以赋值给任意类型。开启后,除了明确声明为 nullundefined 类型,或者使用联合类型(如 string | null),否则不能将 nullundefined 赋值给其他类型。 比如:
let str: string;
str = null; // 开启 strictNullChecks 后会报错

正确的做法是:

let str: string | null;
str = null; // 这样就不会报错

(三)路径和模块解析

  1. baseUrl baseUrl 用于指定模块解析的基础路径。当设置了 baseUrl 后,在导入模块时,相对路径会相对于这个基础路径进行解析。 例如,在项目结构如下:
project/
├── src/
│   ├── utils/
│   │   ├── mathUtils.ts
│   ├── main.ts
├── tsconfig.json

如果 tsconfig.json 中有如下配置:

{
    "compilerOptions": {
        "baseUrl": "./src"
    }
}

那么在 main.ts 中导入 mathUtils.ts 可以这样写:

import { add } from 'utils/mathUtils';

而不是使用相对路径 import { add } from './utils/mathUtils';

  1. paths paths 选项允许你为模块导入定义自定义的路径映射。这在一些复杂项目中非常有用,比如你可能希望使用别名来导入模块。 假设项目结构和上述类似,在 tsconfig.json 中添加如下配置:
{
    "compilerOptions": {
        "baseUrl": "./src",
        "paths": {
            "@utils/*": ["utils/*"]
        }
    }
}

这样在 main.ts 中就可以使用别名导入:

import { add } from '@utils/mathUtils';

(四)其他常用选项

  1. outDir outDir 选项指定编译后的 JavaScript 文件输出的目录。例如,如果项目源文件在 src 目录,你可以将编译后的文件输出到 dist 目录:
{
    "compilerOptions": {
        "outDir": "./dist"
    }
}
  1. rootDir rootDir 用于指定项目的根目录,编译器会从这个目录开始查找需要编译的文件。如果不设置,编译器会自动推断。但在一些复杂项目结构中,明确设置 rootDir 可以避免不必要的文件被编译。 比如项目结构为:
project/
├── src/
│   ├── app/
│   │   ├── main.ts
│   ├── test/
│   │   ├── test.ts
├── tsconfig.json

如果只希望编译 src/app 目录下的文件,可以设置:

{
    "compilerOptions": {
        "rootDir": "./src/app"
    }
}

三、include 和 exclude 配置项

  1. include include 用于指定哪些文件或目录需要被编译器包含在编译范围内。它是一个字符串数组,每个元素可以是文件路径或目录路径,支持通配符。 例如,假设项目结构如下:
project/
├── src/
│   ├── components/
│   │   ├── Button.tsx
│   │   ├── Input.tsx
│   ├── main.tsx
├── test/
│   ├── Button.test.tsx
│   ├── Input.test.tsx
├── tsconfig.json

如果只希望编译 src 目录下的文件,可以在 tsconfig.json 中这样配置:

{
    "include": [
        "src/**/*.tsx",
        "src/**/*.ts"
    ]
}

这里 ** 表示匹配任意层级的子目录,*.tsx*.ts 分别表示匹配所有的 TypeScript 和 TypeScriptX(用于 React 项目)文件。

  1. exclude excludeinclude 相反,用于指定哪些文件或目录需要被编译器排除在编译范围之外。同样是字符串数组,支持通配符。 在上述项目结构中,如果不希望编译 test 目录下的测试文件,可以这样配置:
{
    "exclude": [
        "test/**/*.tsx",
        "test/**/*.ts"
    ]
}

如果同时设置了 includeexcludeexclude 的优先级更高,即即使某个文件在 include 中匹配,但如果在 exclude 中也匹配,该文件将不会被编译。

四、extends 配置项

extends 选项允许一个 tsconfig.json 文件继承另一个配置文件的设置。这在多个项目或项目的不同部分需要共享一些基础配置时非常有用。 例如,假设团队有一个基础的 tsconfig.base.json 文件,包含一些通用的配置:

{
    "compilerOptions": {
        "target": "es2019",
        "module": "esnext",
        "strict": true
    }
}

然后在具体项目的 tsconfig.json 文件中,可以通过 extends 来继承这些配置:

{
    "extends": "./tsconfig.base.json",
    "compilerOptions": {
        "outDir": "./dist",
        "rootDir": "./src"
    },
    "include": [
        "src/**/*.ts"
    ]
}

这样,具体项目的配置文件就包含了 tsconfig.base.json 的所有配置,同时还可以根据项目需求添加或覆盖一些配置项。

五、tsconfig.json 在不同场景下的应用

(一)Node.js 项目

在 Node.js 项目中使用 TypeScript 时,tsconfig.json 的配置通常会侧重于 CommonJS 模块系统和对 Node.js 环境的兼容性。

{
    "compilerOptions": {
        "target": "es2019",
        "module": "commonjs",
        "outDir": "./dist",
        "rootDir": "./src",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
    },
    "include": [
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules",
        "test"
    ]
}

这里 esModuleInterop 设置为 true 是为了更好地支持 ES6 模块与 CommonJS 模块的互操作性,因为 Node.js 原生支持 CommonJS 模块,而 TypeScript 代码可能会使用 ES6 模块语法。skipLibCheck 设置为 true 可以跳过对声明文件(.d.ts)的类型检查,提高编译速度,因为在 Node.js 项目中,很多第三方库的声明文件可能已经经过了充分测试。forceConsistentCasingInFileNames 设置为 true 可以确保在导入模块时文件名的大小写一致性,避免在不同操作系统上出现问题。

(二)React 项目

对于 React 项目,除了基本的 TypeScript 配置,还需要考虑对 JSX 的支持。

{
    "compilerOptions": {
        "target": "es2019",
        "module": "esnext",
        "jsx": "react",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "baseUrl": "./src",
        "paths": {
            "@components/*": ["components/*"],
            "@utils/*": ["utils/*"]
        }
    },
    "include": [
        "src/**/*.tsx",
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules",
        "test"
    ]
}

jsx 选项设置为 react 表示支持 React 的 JSX 语法,编译器会将 JSX 代码转换为相应的 JavaScript 代码。同时,通过 baseUrlpaths 设置了模块导入的别名,方便在 React 项目中更简洁地导入组件和工具函数。

六、常见问题及解决方法

  1. 编译报错:找不到模块 这可能是由于模块解析路径配置不正确导致的。检查 baseUrlpaths 的设置是否符合项目的目录结构。例如,如果 baseUrl 设置错误,导入模块时的相对路径解析就会出错。 解决方法是仔细核对项目目录结构,确保 baseUrl 设置为正确的基础路径,并且 paths 中的路径映射与实际文件位置一致。
  2. 编译后的代码在运行时出现类型错误 这种情况可能是因为 strict 模式没有完全启用,或者在某些地方使用了不严谨的类型声明。例如,可能有未明确类型的变量在运行时出现了类型不匹配的情况。 解决方法是开启 strict 模式,并仔细检查代码中的类型声明,确保所有变量、函数参数和返回值都有明确且正确的类型。同时,注意在使用第三方库时,确保其类型声明文件与项目的类型设置兼容。

七、优化编译性能

  1. 使用 skipLibCheck 正如前面提到的,设置 skipLibChecktrue 可以跳过对声明文件(.d.ts)的类型检查。这在项目中使用了大量第三方库且其类型声明文件比较稳定时,可以显著提高编译速度。但要注意,如果第三方库的类型声明文件有问题,可能会在运行时才发现类型错误。
{
    "compilerOptions": {
        "skipLibCheck": true
    }
}
  1. 合理设置 includeexclude 精确地指定需要编译的文件和目录,可以减少编译器的工作范围,从而提高编译性能。避免不必要地包含一些测试文件、工具文件等不需要在生产环境中编译的文件。例如,在上述 Node.js 和 React 项目的配置示例中,通过合理设置 exclude 排除了 node_modulestest 目录。
  2. 启用 isolatedModules isolatedModules 选项可以让编译器以独立模块的方式编译每个文件,这在一些情况下可以提高编译性能,特别是在项目文件数量较多时。开启该选项后,每个文件的编译过程相对独立,不会因为某个文件的类型错误影响其他文件的编译。
{
    "compilerOptions": {
        "isolatedModules": true
    }
}

不过需要注意的是,启用 isolatedModules 后,一些依赖于全局类型声明的代码可能会出现问题,需要开发者在编写代码时更加注意类型的作用域。

通过对 tsconfig.json 文件各个配置项的深入理解和合理设置,我们可以更好地控制 TypeScript 项目的编译过程,提高代码质量和开发效率,同时优化编译性能,使其在不同的前端开发场景中都能发挥出最大的优势。无论是 Node.js 项目还是 React 项目,根据项目的具体需求灵活调整 tsconfig.json 的配置是前端开发中不可或缺的技能。在实际开发中,不断积累经验,根据项目的变化适时调整配置,将有助于打造出更加健壮、高效的前端应用。