最小 tsconfig.json

最后更新于

TypeScript 发展至今积累了许多过时配置项以及重复配置项,本文试图按现在最新的文档,给出最小的 tsconfig.json 配置。

TL;DR

{
  "compilerOptions": {
    "strict": true,
    "target": "ESNext"
  }
}

如果你有第三方库,建议增加以下配置:

{
  "compilerOptions": {
    "strict": true,
    "target": "ESNext",
    "module": "Preserve",
    "types": [],
    "skipLibCheck": true,
    "noEmit": true,
    "verbatimModuleSyntax": true
  }
}

解释

strict

开启严格模式,比如不允许含有 null 的类型通过检查。

declare const const a: string | nulla: string | null
a.String.length: numberlength
'a' is possibly 'null'.

这个严格模式代替了其他 8 个建议开启的配置项,我就不一一列举了,文末会附上一些更严格的选项,用户按需打开。

target

代码降级和默认 lib,类比 esbuild 的 target,不填是 ES5。例如,当 targetES5 时,let a = 1 会变成 var a = 1。如果不使用 tsc 编译出 js 文件,这里的降级对我们没有意义。

targetES6 及以上时,会默认设置:

因此大多数情况下,你只需要设置 targetESNext 即可使用最新的语法和 ESM 环境。

module

使用哪种模块类型,是 ESM 还是 CJS,类比 esbuild 的 format。不填时,当 targetES6 以上时默认是为 ES6

由于现代前端主流就是推行 ESM,这里设置为以下任何一个变体的即可。

module说明
ES6/ES2015支持 import / export
ES2020增加了 import() / import.meta 支持
ES2022/ESNext增加了 top level await 支持
Node16/NodeNext等于 ES2020/ESNext,但是强制要求文件名后缀匹配
Preserve交给用户或者打包器决定,不再产生报错

moduleResolution

要求按何种算法搜索 import 后面的路径,使用现代打包器的建议设置为 Bundler

当你使用 module: "Preserve" 时,moduleResolution: "Bundler" 默认开启。

esModuleInterop

影响如何使用第三方库的默认导出名,使用现代打包器的这里无脑打开即可。

如果你没有第三方库,根本无需这个选项。

当你使用 module: "Preserve" 时,esModuleInterop: true 默认开启。

types: ["node"]

设置导入哪些污染全局的类型声明,比如 @types/node。默认不填的话所有 node_modules/@types 里的类型都会注入到全局,如果你觉得这个行为慢或者不安全,可以手动设置。

skipLibCheck

所有 d.ts 文件不再报告错误或警告,通常是用来规避一些第三方库的类型冲突问题。

noEmit

打开这个选项主要是为了规避文件名冲突问题,tsc 默认会假设你会运行他来生成目标 js 文件,如果这个生成会覆盖已有文件就会报错。你可以看情况打开这个选项告诉他不会生成 js。

verbatimModuleSyntax

强制要求类型导入必须使用 import type,方便第三方打包器分析文件依赖的时候不产生循环引用。

附:include/exclude/files

这几个选项会用来决定当前项目里有哪些文件,TypeScript 会使用以下几种行为。

因此最无脑的选择是直接把 tsconfig.json 放到 src 目录里。

附:超级严格选项

noFallthroughCasesInSwitch: true

不允许直接 fallthrough 到下一个 case,除非用 // @ts-expect-error 绕过。

const const a: numbera: number = 6

switch (const a: numbera) {
  case 0:
Fallthrough case in switch.
var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log('even') case 1: var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log('odd') break } switch (const a: numbera) { // @ts-expect-error case 0: var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log('even') case 1: var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log('odd') break }
noImplicitOverride: true

不允许直接覆盖父类的方法,除非加一个 override 关键字。

noImplicitReturns: true

不允许不写 return

noPropertyAccessFromIndexSignature: true

不允许用 . 访问未知下标属性。

noUncheckedIndexedAccess: true

访问未知下标属性的时候会自动加上 | undefined

noUnusedLocals: true

不允许出现未使用的局部变量,真有可以加前缀 _

noUnusedParameters: true

不允许出现未使用的参数,真有可以加前缀 _


更新

TypeScript 还有一个无配置文件模式,当然这个模式下默认是不开 strict 的,来看看实际上会造成什么问题:

  1. target: "es5",不使用 tsc 编译的话其实没啥影响
  2. alwaysStrict: false,默认不产生 "use strict"
  3. noImplicitAny: false,隐式 any 不会报错,所以你可以写下面这个东东
    window.foo = 'bar'
    // 否则你必须标记成
    ;(window as any).foo = 'bar'
  4. noImplicitThis: falsethis 不标类型不会报错,所以你可以写下面这个东东
    function foo() {
      return this.bar
    }
    // 否则你必须标记成
    function foo(this: { bar: unknown }) {
      return this.bar
    }
  5. strictNullChecks: false,不会检查 null | undefined,但是目前 VS Code 和 Sublime LSP TypeScript 都是默认开启这个检查的 (有可能是 tsserver 默认开启),所以实际上可以理解成仍然会检查 null。下面这段代码可以用 tsc 编译过但是常见编辑器里会有提示
    declare const const a: string | nulla: string | null
    var console: Consoleconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)log(a.String.toLowerCase(): stringtoLowerCase())
    'a' is possibly 'null'.
  6. strictPropertyInitialization: false,不会检查未初始化的属性
    class Foo {
      bar: string
      constructor() {
        // Not initializing 'bar' here and no error
      }
    }
  7. useUnknownInCatchVariables: falsecatch(error) 默认用 any 类型。其实在规范的项目里我希望 error 直接隐式标成 Error,这样 .message 比较容易
  8. 你可以正常使用 import { uniq } from "lodash"import.meta.url 等语法
  9. 但是不能写 top level await,这个功能必须要设置 module: "ESNext" / "ES2022"

更新

最近 TypeScript 5.4 新增了一个 module: "Preserve" 配置项,可以让 TypeScript 在看到混合语法时不报错(通常这是由作者自己的打包器处理的),并且省下三个常用配置项:

"moduleResolution": "bundler",
"esModuleInterop": true,
"resolveJsonModule": true,

另外,我将常用的 tsconfig.json 发到了一个包里:https://github.com/hyrious/configs