日期:2021年9月18日标签:JavaScript

详解tsconfig.json文件 #

个人很喜欢TypeScirpt,用Ts写代码要比Js舒服太多,可以大大提高写代码的效率,减少代码维护的工作量。因为有C++、C#的基础,我上手Ts的速度也很快。虽然使用起来并没有什么困难,但是很长一段时间,没有去了解typescirpt的编译过程及配置,所以自己也不能算是真正的了解它。

工欲善其事必先利其器,如果你对ts的配置及编译过程不是很了解的话,那么这篇文章应该会对你有所帮助。

接下来的内容需要你的环境安装TypeScript,请确保安装了TypeScript,否则tsc命令无法识别。

一.tsc #

Ts提供了一个命令行功能tsc,使用它可以将Ts文件编译成Js文件。

首先创建一个文件夹和index.ts文件,进入learnTsConfig文件夹。

mkdir learnTsConfig
mkdir learnTsConfig/src
touch learnTsConfig/src/index.ts

cd learnTsConfig

可以在index.ts文件中输入一些Ts代码,例如:

// src/index.ts
type FuncType = () => void;

const sayHello: FuncType = () => {
    console.log("Hello Ts");
};

然后使用tsc [ts file]命令编译文件:

tsc src/index.ts

可以看到,在src/index.ts同目录下,有个index.js文件生成,该文件就是编译后生成的文件,src/index.js文件内容如下:

var sayHello = function () {
    console.log("Hello Ts");
};

为tsc命令指定ts文件,不适用于项目中,我们不可能将所有要编译的文件路径都添加在tsc命令后面,所以通常用JSON配置文件(tsconfig.json)配置tsc编译器。当执行tsc命令时,ts编译器会首先在当前目录寻找tsconfig.json文件,如果当前目录没有tsconfig.json文件,它会一直向上级目录寻找,直到找到一个tsconfig.json文件,tsconfig.json文件所在的目录为项目的根目录

如果最终没有找到一个tsconfig.json文件,执行tsc命令将不会编译任何文件,只会输出版本号及help信息。

下面详细介绍tsconfig.json文件。

二.tsconfig.json文件 #

tsconfig.json文件支持JSON5的内容,所以可以在tsconfig.json文件中添加注释,使用单引号代。

tsconfig.json文件结构如下:

{
    "files": [],
    // ...
    "compilerOptions": {
        "target": "ES6",
        "module": "CommonJS",
        "outDir": "./dist/development"
    }
}

下面我将配置选项分为编译选项(compilerOptions字段下的配置项)和非编译选项(compilerOptions字段外的根级选项)进行介绍。

首先在learnTsConfig目录下,新建tsconfig.json文件。

touch tsconfig.json

非编译选项 #

非编译选项通常控制的是typescript编译器要编译的项目(文件)信息,例如要编译的ts文件。

  1. files

files选项用于指示哪些文件需要编译,可以添加一组文件路径,支持相对路径和绝对路径,相对路径是相对于项目根目录的,即tsconfig.json文件所在的目录,建议大家使用相对路径,避免使用绝对路径,这样即使项目换了环境也不用更改配置。

在根目录下新建文件outerIndex.ts文件,此时目录结构如下

learnTsConfig/
    src/
        index.ts
    outerIndex.ts
    tsconfig.json

在tsconfig.json文件中输入以下内容:

{
    // files选项用于设置需要编译的文件
    "files": [
        "outerIndex.ts",
        "src/index.ts"
    ]
}

执行tsc命令进行编译,编译后的文件结构如下:

learnTsConfig/
    src/
        index.ts
        index.js
    outerIndex.ts
    outerIndex.js
    tsconfig.json
  1. include & exclude

如果项目中文件较少的情况下,使用files选项设置要编译的目标文件是一个不错的选择,但是当文件特别多或者项目文件更新频繁的时候,将所有要编译的文件都写在files数组中就比较麻烦,每次添加一个ts文件,都要更新files选项。

所以ts还提供了include选项,这个选项与files类似,但是你可以使用模式匹配,删掉之前编译的js文件,更改tsconfig.ts文件内容如下:

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

执行tsc编译,文件结构如下,仅src/目录下的index.ts被编译。

learnTsConfig/
    src/
        index.ts
        index.js
    outerIndex.ts
    tsconfig.json

exclude作用于include正好相反,用于排除某些文件,也支持模式匹配,清理js文件,更新tsconfig.ts文件内容如下:

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

include选项表示,编译根目录下的所有ts文件,exclude表示排除src/目录下所有ts文件,所以tsc编译后的文件结构如下:

learnTsConfig/
    src/
        index.ts
    outerIndex.ts
    outerIndex.js
    tsconfig.json

在用模式匹配时,可以不用显示添加.ts后缀,默认情况下typescript会自动寻找.ts文件和.d.ts文件,如果你设置了allowJS编译选项(后面介绍编译选项内容)为true,typescript还会寻找.js文件。src/**/*等价为src/**/*.tssrc/**/*.d.ts(如果allowJs为true,还包括src/**/*.js)。

有些文件夹,typescript会自动排除,例如node_modulesbower_componentsjspm_packages<outDir>。如果你要强制ts编译这些文件夹中的ts文件,需要指定files文件选项。

编译选项 #

编译选项即compilerOptions字段下的相关配置。

输出文件 #

  1. outDir

默认情况下,ts编译后的js文件,与源文件都在同一个目录下。使用outDir选项可以指定编译后的文件所在的目录。清理之前编译生成的js文件。

learnTsConfig/
    src/
        index.ts
    outerIndex.ts
    tsconfig.json

更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist"
    }
}

然后执行tsc命令进行编译,编译后会生成dist/文件夹,dist/目录下即编译生成的js文件,目录下的结构与项目目录结构相同。

learnTsConfig/
    dist/
        src/
            index.js
        outerIndex.js
    src/
        index.ts
    outerIndex.ts
    tsconfig.json
  1. rootDir

typescirpt项目的默认的根目录为tsconfig.json文件所在的目录,所有的相对路径都是相对于这个根目录的。我们可以通过rootDir选项,更改项目的根目录位置。删除dist文件夹,更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "rootDir": ".."
    }
}

执行tsc命令,编译后的目录如下:

learnTsConfig/
    dist/
        learnTsConfig/
            src/
                index.js
            outerIndex.js
    src/
        index.ts
    outerIndex.ts
    tsconfig.json

大多数情况下,我们保持TypeScript默认行为即可,使用tsconfig.json文件所在位置为项目的根目录。

  1. removeComments

removeComments选项用于删除编译后的js文件中的注释代码。

首先清理项目,更新tsconfig.json文件如下:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
    }
}

更新src/index.ts文件内容:

type FuncType = () => void;

// print info: Hello Ts
const sayHello: FuncType = () => {
    console.log("Hello Ts");
};

tsc编译,编译后的dist/src/index.js文件内容如下:

// print info: Hello Ts
var sayHello = function () {
    console.log("Hello Ts");
};

可以看到注释内容并未去掉。更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true
    }
}

再次编译,编译后的dist/src/index.js文件如下:

var sayHello = function () {
    console.log("Hello Ts");
};

可以看到,注释内容被删掉了。

  1. module

假设你正在开发的项目,需要在nodejs环境中运行,在项目中你使用了import引入模块,但是nodejs并不支持,nodejs使用的是CommonJS模块系统。为了使编译后的js文件import语句转换为require语句,你可以配置"module": "CommonJS"

下面,我们手动试一下。更新tsconfig.js文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "module": "CommonJS"
    }
}

在src目录下添加add.ts文件,内容如下:

/**
 * Add two numbers
 * @param a a number
 * @param b a number
 * @returns {number} sum
 */
export const add = (a: number, b: number):number => {
    return a + b;
};

更新src/index.ts文件:

import {add} from "./add"

console.log(add(3, 5));

编译文件,查看dist/src/index.js内容如下,可以看到import语句已经转换为require语句。

"use strict";
exports.__esModule = true;
var add_1 = require("./add");
console.log(add_1.add(3, 5));
  1. outFile

outFile可以指定编译后的结果文件被打包成一个bundle,即一个js文件,前提是module选项被设置成System或者AMD。如果想要支持其他的module选项,可以借助webpackparcel等工具。

简单的尝试一下,更新tsconfig.json文件如下:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outFile": "dist/bundle.js",
        "removeComments": true,
        "module": "AMD"
    }
}

编译后仅有dist/bundle.js文件生成。

source map #

  1. sourceMap

首先,简单介绍下source-map文件。source-map文件表示编译的源文件与输出的结果文件的一种映射关系,在项目调试时起到非常重要的作用,可以让我们在浏览器开发者工具中显示源文件代码进行调试,要知道浏览器运行的是编译后的代码,如果调试看着编译后的代码,那么调试是一件多么痛苦的事啊。

source-map文件由编译工具自动生成,以.map后缀结尾,它是一个JSON文件,结构如下。

{
    "version": 3,
    "file": "bundle.js",
    "sourceRoot": "",
    "sources": [
        "../outerIndex.ts",
        "../src/add.ts",
        "../src/index.ts"
    ],
    "names": [],
    "mappings": "AAAA,IAAM,SAAS..."
}

file字段表示与该source-map文件对应的输出文件名称,sources字段表示的是编译源文件。mapping属性是base64编码值,表示源文件与输出文件之间的关系。

而编译后输出的文件末尾会有sourceMappingURL字段的注释,表示map文件位置,这个注释会被浏览器读取,用于定位map文件。

// b.js
var b = 'B';

//# sourceMappingURL=b.js.map 

为了生成source-map文件,可以设置sourceMap字段为true。

更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outFile": "dist/bundle.js",
        "removeComments": true,
        "module": "AMD",
        "sourceMap": true
    }
}

执行tsc命令,编译后会生成dist/bundle.jsdist/bundle.js.map

  1. inlineSourceMap

sourceMap选项,会为编译后的js文件生成一个分离的.map文件。设置inlineSourceMap为true的话,可以避免生成.map文件,将map直接内嵌在编译后的js文件中。

更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outFile": "dist/bundle.js",
        "removeComments": true,
        "module": "AMD",
        "inlineSourceMap": true
    }
}

进行编译后,查看编译后的dist/bundle.js文件:


...

//# sourceMappingURL=data:application/json;base64,...

可以看到sourceMappingURL后面直接紧跟着base64数据(map文件内容)。

类型声明 #

typescript最重要的作用是使js支持类型,变成一种强类型语言。

  1. declaration

设置declaration字段为true,可以自动生成声明文件。

更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true
    }
}

执行tsc命令编译,可以看到dist/**/*.d.ts文件生成,该文件是对应的*.js文件的的声明文件。通过这些声明文件,ts编译器可以知道package的API结构,即使编译后的JavaScript文件不包含任何类型信息。

生成的dist文件目录如下:

dist/
    src/
        add.d.ts
        add.js
        index.d.ts
        index.js
    outerIndex.d.ts
    outInder.js
  1. declarationDir

declaration设置为ture时,ts编译器将对应的.d.ts文件放入到编译后的js文件同级目录下。可以通过设置declarationDir将所有声明文件放到同一个目录下。

更新tsconfig.json文件如下:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
        "declarationDir": "dist/types", 
    }
}

执行tsc命令,可以看到dist/types/目录生成,目录下结构与源文件目录相同,只不过都是.d.ts声明文件。

  1. lib

typescirpt内置了一些TypeScirpt声明文件,例如PromiseObject.freeze和浏览器的API的声明。这些声明文件可以提供代码提示和警告功能。通常存放在lib文件夹下,我们称之为标准库。通过设置lib字段,手动选择导入哪些库。

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
        "declarationDir": "dist/types", 
        "lib": [ "ES5", "ES2015.Promise", "DOM" ]
    }
}
  1. typeRoots

当你将你的typescript程序,发布为npm包时,你可能发布的是编译后的JavaScript包,这样非typescript程序可以引入该npm包,为了提供声明信息,你也可以提供对应的声明文件,这样typescirpt编译器和IDE(例如vscode)可以根据声明文件,提供类型提示、错误提示。为了生成声明文件,你只需要将declaration设置为true即可。

但是并不是所有的程序都是typescript写的,例如lodash,它本身就是javascript编写的,所以无法使用ts提供的功能,自动生成声明文件。所以需要手动为没有声明文件的npm包提供声明文件。

DefinitelyTyped社区的工作就是为那些比较流行的没有声明文件的npm包,提供声明文件。你可以通过安装以@types开头的声明文件包为某个npm包提供声明文件。

例如npm install @types/lodash@types/lodash就是lodash包的声明文件。

默认情况下,typescript会从node_modules/@types文件夹下导入所有类型声明至全局空间,需要注意只会导入script文件中的声明至全局空间,module文件对全局空间是隐藏的。

type-root就是包的声明文件存储的目录,可以通过typeRoots选项设置一系列为文件路径,指示typescript从哪些地方导入类型信息,默认值为node_modules/@types

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
        "declarationDir": "dist/types", 
        "typeRoots": ["./my-types"]
    }
}

通过设置"typeRoots": ["./my-types"],typescript将会只从my-types文件夹下导入声明信息,而不是从node_modules文件夹下。

  1. types

typeRoots用于导入目录下所有的声明至全局空间,但是如果设置了types,则只会导入types指定的包声明至全局空间。

{
    "compilerOptions": {
        "outDir": "dist",
        "types": [ "node", "moment" ] // 导入node_modules文件夹下的@types/node和@types/moment,其他包将会被忽略
    }
}

JavaScript编译 #

默认情况下,typescript编译是不包括js文件的。可以通过allowJScheckJS更改这一默认行为。

  1. allowJS

当你在typescript文件中使用import语句引入模块时,例如import {add} from "./add,默认情况下ts编译器会自动寻找src/add.tssrc/add.d.ts,它不会去考虑src/add.js,我们可以通过将allowJS设置为true来更改这一默认行为。

首先,更新tsconfig.json文件如下:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
    }
}

新建src/add.js,输入以下代码:

export const add = (a, b) => {
    return a + b;
};

src/index.js中引入add函数:

import {add} from "./add";

console.log(add(3, 5));

默认情况下,allowJSfalse,所以此时进行tsc编译后,编译后的dist/src/目录下是不会包含add.js文件的。

更新tsconfig.json文件,将allowJS设置为true。

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
        "allowJs": true
    }
}

重新编译,编译后dist/src/目录下会新增add.d.ts文件和add.js文件。

所以如果,你想要的你的项目支持js文件,只需要设置"allowJs": true即可。

  1. checkJs

使用allowJS可以让编译器在编译阶段包含js文件,但是编译器并不会对js文件进行类型检查。类型检查是ts的一个重要的特性,有了类型检查,大大提高了我们的开发效率,所以为了能工让ts编译器对js文件进行类型检查,需要设置"checkJS": true

为了进一步解释,首先在add.js的顶部添加const num = parseInt(1.5);

然后使用tsc编译,编译成功。

然后更新tsconfig.json文件:

{
    "include": [
        "**/*.ts",
    ],
    "compilerOptions": {
        "outDir": "dist",
        "removeComments": true,
        "declaration": true,
        "allowJs": true,
        "checkJs": true
    }
}

再使用tsc编译,编译应该会报错。

src/add.js:1:22 - error TS2345: Argument of type '1.5' is not assignable to parameter of type 'string'.

1 const num = parseInt(1.5);
                       ~~~

Found 1 error.

因为此时checkJS为true,ts编译器会对js文件进行类型检查,parseInt函数接受的参数为字符串,但是1.5number类型,类型不兼容,所以编译失败。如果你是用的是vscode编辑器,开启了checkJS后,在你敲const num = parseInt(1.5);这句代码时,编辑器应该会给出智能错误提示。

三.总结 #

tsconfig的配置字段有很多,本篇内容旨在介绍它的基本用法和基本结构,并不包含所有的内容。希望通过这篇文章,你能了解tsc编译器,知道如何去配置tsc编译器,理解它的原理。

更多的tsconfig配置,请查阅TSConfig Reference

附:参考资料 #

(完)

目录