个人很喜欢TypeScirpt,用Ts写代码要比Js舒服太多,可以大大提高写代码的效率,减少代码维护的工作量。因为有C++、C#的基础,我上手Ts的速度也很快。虽然使用起来并没有什么困难,但是很长一段时间,没有去了解typescirpt的编译过程及配置,所以自己也不能算是真正的了解它。
工欲善其事必先利其器,如果你对ts的配置及编译过程不是很了解的话,那么这篇文章应该会对你有所帮助。
接下来的内容需要你的环境安装TypeScript,请确保安装了TypeScript,否则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文件。
filesfiles选项用于指示哪些文件需要编译,可以添加一组文件路径,支持相对路径和绝对路径,相对路径是相对于项目根目录的,即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
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/**/*.ts和src/**/*.d.ts(如果allowJs为true,还包括src/**/*.js)。
有些文件夹,typescript会自动排除,例如node_modules、bower_components、jspm_packages和<outDir>。如果你要强制ts编译这些文件夹中的ts文件,需要指定files文件选项。
编译选项即compilerOptions字段下的相关配置。
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
rootDirtypescirpt项目的默认的根目录为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文件所在位置为项目的根目录。
removeCommentsremoveComments选项用于删除编译后的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");
};
可以看到,注释内容被删掉了。
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));
outFileoutFile可以指定编译后的结果文件被打包成一个bundle,即一个js文件,前提是module选项被设置成System或者AMD。如果想要支持其他的module选项,可以借助webpack、parcel等工具。
简单的尝试一下,更新tsconfig.json文件如下:
{
"include": [
"**/*.ts",
],
"compilerOptions": {
"outFile": "dist/bundle.js",
"removeComments": true,
"module": "AMD"
}
}
编译后仅有dist/bundle.js文件生成。
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.js和dist/bundle.js.map。
inlineSourceMapsourceMap选项,会为编译后的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支持类型,变成一种强类型语言。
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
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声明文件。
libtypescirpt内置了一些TypeScirpt声明文件,例如Promise、Object.freeze和浏览器的API的声明。这些声明文件可以提供代码提示和警告功能。通常存放在lib文件夹下,我们称之为标准库。通过设置lib字段,手动选择导入哪些库。
{
"include": [
"**/*.ts",
],
"compilerOptions": {
"outDir": "dist",
"removeComments": true,
"declaration": true,
"declarationDir": "dist/types",
"lib": [ "ES5", "ES2015.Promise", "DOM" ]
}
}
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文件夹下。
typestypeRoots用于导入目录下所有的声明至全局空间,但是如果设置了types,则只会导入types指定的包声明至全局空间。
{
"compilerOptions": {
"outDir": "dist",
"types": [ "node", "moment" ] // 导入node_modules文件夹下的@types/node和@types/moment,其他包将会被忽略
}
}
默认情况下,typescript编译是不包括js文件的。可以通过allowJS和checkJS更改这一默认行为。
allowJS当你在typescript文件中使用import语句引入模块时,例如import {add} from "./add,默认情况下ts编译器会自动寻找src/add.ts和src/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));
默认情况下,allowJS是false,所以此时进行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即可。
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.5是number类型,类型不兼容,所以编译失败。如果你是用的是vscode编辑器,开启了checkJS后,在你敲const num = parseInt(1.5);这句代码时,编辑器应该会给出智能错误提示。
tsconfig的配置字段有很多,本篇内容旨在介绍它的基本用法和基本结构,并不包含所有的内容。希望通过这篇文章,你能了解tsc编译器,知道如何去配置tsc编译器,理解它的原理。
更多的tsconfig配置,请查阅TSConfig Reference。
(完)