TypeScript 配置文件 (tsconfig.json)

在前面所有的学习中,我们都是通过一条简单的 “tsc test.ts” 命令,手动将一个独立的 TypeScript 文件编译成 JavaScript 来运行的。

但在真实的项目中(通常包含成百上千个文件),我们不可能每次都去手动输入一大堆带有复杂参数的编译命令(比如 tsc app.ts --target ES6 --strict true)。

我们更需要一个全局的 “指挥中心” 来统一管理整个项目的编译规则。这个指挥中心,就是大名鼎鼎的 tsconfig.json 文件。

TypeScript tsconfig.json 文件简介

如果想要在一个项目中初始化 TypeScript 的配置文件,我们只需要在项目根目录下,打开终端并执行以下命令即可:

tsc --init

执行完毕之后,项目根目录下就会多出一个 tsconfig.json 文件(如下图所示)。

创建 tsconfig.json

只要你的项目根目录包含了这个文件,就相当于告诉编辑器:这个项目是一个 TypeScript 项目,请 TypeScript 编译器在工作时,严格遵守这个文件里定义的规则。

接下来,当我们直接在终端输入 “tsc”(不需要加任何文件名)时,编译器就会自动读取该文件,并对整个项目进行全量编译。

打开生成的 tsconfig.json 文件,我们会发现里面全是配置项。我们先跳过最复杂的 compilerOptions,来看看在这个 JSON 文件的最外层(顶层),有哪几个决定文件命运的关键属性。

顶层文件的匹配规则:

{
    "compilerOptions": {
        // ...复杂的编译选项,稍后详讲
    },
    // include:需要编译哪些文件(支持 glob 匹配通配符)
    "include": [
        "src/**/*"
    ],
    // exclude:不要编译哪些文件
    "exclude": [
        "node_modules",
        "dist",
        "**/*.spec.ts"
    ]
}

"include" 选项用于告诉 TypeScript 需要编译哪些文件,其中 "src//*" 表示匹配 src 目录下的所有文件(递归查询)。

"exclude" 选项用于告诉 TypeScript 千万不要编译哪些文件。默认情况下,即便不显式写 exclude,TypeScript 也会非常聪明地自动帮我们排除掉 node_modules 目录。

tsconfig.json 文件中的 compilerOptions 对象

在 tsconfig.json 文件中,compilerOptions 是最核心、最庞大的对象,它直接决定了 TypeScript 的 “行为表现” 和 “输出产物”。

针对日常开发,可以分为以下 6 大核心的配置组。

1. 模块与产物 (target / module / outDir)

这一组配置决定了 TypeScript 代码编译成 JavaScript 后,到底长什么样子,以及存放在哪里。

{
    "compilerOptions": {
        // target: 指定编译后的 ECMAScript 目标版本。
        // 为了兼容旧版浏览器,通常设为 "ES2015" (即 ES6) 或 "ES5"。
        // 在现代打包工具(Vite)环境中,通常设为 "ESNext"(最新版)。
        "target": "ES2015",

        // module: 指定编译后代码使用的模块化标准。
        // 开发 Node.js 应用通常用 "CommonJS";现代前端开发通常用 "ESNext"。
        "module": "ESNext",

        // outDir: 编译后的 JS 文件统一输出到哪个目录下。
        "outDir": "./dist",

        // rootDir: 指定 TS 源码的根目录,用于在 outDir 中维持原本的目录结构。
        "rootDir": "./src"
    }
}

2. 路径别名 (baseUrl & paths)

如果小伙伴们已经接触过 Vue 或 React,那么肯定对下面这种高逼格的导入方式比较熟悉:

import UserCard from "@/components/UserCard"

这里的 “@” 就是一个路径别名。像这种路径别名,如果我们不在 tsconfig.json 中显式配置它,那么 TypeScript 编译器就会立刻给你爆红,因为它根本不认识 “@” 是个什么。

{
    "compilerOptions": {
        // baseUrl: 解析非相对模块名的基准目录(通常设为 "." 代表项目根目录)
        "baseUrl": ".",
        
        // paths: 路径别名的核心映射表(必须配合 baseUrl 使用)
        "paths": {
            "@/*": ["src/*"],
            "@utils/*": ["src/utils/*"]
        }
    }
}

此外需要注意的是,这里配置的 paths 仅仅是让 TypeScript 的 “类型检查器” 不报错,并提供智能提示。但如果要让项目真正跑起来,我们还需要在打包工具(如Vite 的 resolve.alias 或 Webpack 的 alias)中配置完全一样的映射才行。

3. 类型安全(strict)

TypeScript 最强大的地方就在于它的 “严格检查”。官方提供了一个非常暴力的 “总闸”开关:strict。

{
    "compilerOptions": {
        // strict: 启用所有严格的类型检查选项。这通常是大厂和现代框架的强制要求!
        "strict": true,

        // --- 下面是 strict 为 true 时,底层自动开启的核心子选项(了解即可) ---
        
        // 禁止隐式 any(如果 TS 猜不出类型,且你没写类型,直接报错拦截)
        "noImplicitAny": true,
        
        // 严格空值检查(上一章讲的 NonNullable 就是为了应对它。杜绝 null 导致的运行时崩溃)
        "strictNullChecks": true
    }
}

4. 模块互操作性 (esModuleInterop)

在旧版的 CommonJS 生态(如老版本的 React 库)中,很多库使用的是 module.exports = React 导出的。而现代前端使用 ES Module 导入时,写的是 import React from "react"。

如果不做处理,TypeScript 会直接报错 “该模块没有默认导出”。为了让 CommonJS 和 ES Module 能够完美地 “交朋友”,我们必须开启这个选项:

{
    "compilerOptions": {
        // 允许以 ES Module 的 default 方式,去导入 CommonJS 的模块
        "esModuleInterop": true
    }
}

5. 运行环境库 (lib)

TypeScript 编译器默认只认识最基础的 JavaScript 语法。如果你在代码里写了 document.getElementById("app"),TypeScript 可能会直接报错,因为它不知道 document 是个什么东西(毕竟在纯 Node.js 环境里是没有 DOM 概念的)。

通过配置 lib,我们可以明确告诉 TypeScript 编译器:“我的代码将来是在浏览器里跑的,请把浏览器的 DOM API 也加入到你的类型字典里!”

{
    "compilerOptions": {
        // 引入最新 ES 语法特性,以及浏览器的 DOM/BOM API 提示
        "lib": ["ESNext", "DOM", "DOM.Iterable"]
    }
}

6. 跳过第三方库检查 (skipLibCheck)

这是企业级开发中必须开启的救命选项!

很多时候,我们满心欢喜地通过 npm 安装了一个第三方开源库。结果一编译,满屏全是从 node_modules 里面冒出来的红色类型报错。这根本不是我们业务代码的问题,却卡住了我们打包的进度,让人非常崩溃。

{
    "compilerOptions": {
        // 忽略所有 .d.ts 声明文件的类型检查(尤其是 node_modules 里的)
        // 极大地提升编译速度,并彻底消除第三方库导致的报错
        "skipLibCheck": true
    }
}

TypeScript 企业级完整配置模板

为了方便小伙伴们在实际项目中直接抄作业,我们准备了一份现代前端(Vue / React + Vite)最标准的 tsconfig.json 配置骨架,小伙伴们之间复制过去就能使用。

完整的 tsconfig.json:

{
    "compilerOptions": {
        "target": "ESNext",
        "module": "ESNext",
        // 现代前端(Vite/打包工具)的标准模块解析策略
        "moduleResolution": "bundler",
        "strict": true,
        "jsx": "preserve",
        "sourceMap": true,
        "resolveJsonModule": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "baseUrl": ".",
        "paths": {
            "@/*": ["src/*"]
        }
    },
    "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
    "exclude": ["node_modules", "dist"]
}

此外还需要提一下的是,在一些庞大的 Monorepo(单体多仓库)企业级架构中,如果有多个子项目,每个项目都写一份长长的 tsconfig.json 会非常冗余。

实际上,TypeScript 贴心地为我们提供了 extends 属性,允许我们抽离出一份基础配置,然后在子项目中 “继承” 并覆盖它,比如:

{
    // 继承项目根目录下的基础配置
    "extends": "../../tsconfig.base.json",
    "compilerOptions": {
        "outDir": "./dist"    // 覆盖特定的输出目录
    }
}
给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号