构建 Typescript 知识体系(十三)-tsconfig.json 配置及实战演练

1,313 阅读13分钟

这是我参与更文挑战的第十九天,活动详情查看:更文挑战

如何覆盖默认 TS 配置文件

tsconfig.base.json

首先创建一个tsconfig.base.json并加入如下内容

{
  /*编译器需要编译的单个文件的列表--只有指定的文件会被编译*/
  "files": ["src/a.ts"],
  /*编译器需要编译的文件或者目录*/
  "include": [
    // 编译src下所有文件
    "src"
    // 编译src一级目录下所有文件
    // "src/*",
    // 编译src二级目录下所有文件
    //  "src/*/*"
  ],
  /*需要排除的文件或者目录  默认排除 node-module目录和所有的声明文件*/
  "exclude": ["src/lib"]
  /*
    配置文件时可以继承的,可以把基础的一些配置抽离出来,方便复用
    */
}

tsconfig.json

然后创建tsconfig.json并加入如下内容

{
  "extends": "./tsconfig.base.json",
  /*下面的配置可以覆盖extends的配置*/
  "exclude": [],
  "compileOnSave": false
}

tsconfig.json 详细字段浅析

compilerOptions(编译选项)

字段的详细解释可以参考这篇文档

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */
    /* Basic Options */
    /*
      增量编译,TS编译器在第一次编译后存储一个编译信息相关的文件
      然后二次编译时会根据这个文件做增量编译,可以提高编译的速度
     */
    // "incremental": true,                         /* Enable incremental compilation */
    "target": "es5" /* 用于指定编译之后的目标版本 version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'.*/,
    // 生成代码的模块标准
    "module": "commonjs" /* 用来指定要使用的模块标准: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
    /*TS需要引用的库,即声明文件,ES5默认 dom,es5,scripthost*/
    // "lib": [],                                   /* 用于指定要包含在编译中的库文件,如果你要使用一些ES6的新语法,你需要引入ES6这个库,或者也可以写ES2015 */

    /*允许编译JS文件(js,jsx)*/
    // "allowJs": true,                             /* 是否允许编译JS文件,默认是false,即不编译JS文件 */

    /*允许在JS文件中报错,通常与 allowJs 一起使用*/
    // "checkJs": true,                             /* 是否检查和报告JS文件中的错误,默认是false*/
    // "jsx": "preserve",                           /* jsx代码用于的开发环境: 'preserve', 'react-native', or 'react' */

    /*生成声明文件*/
    // "declaration": true,                         /* 是否在编译的时候生成相应的".d.ts"声明文件。如果设为true,编译每个ts文件之后会生成一个js文件和一个声明文件。但是declaration和allowJs不能同时设为true */

    /*生成声明文件的 sourceMap */
    // "declarationMap": true,                      /* 是否为声明文件.d.ts生成sourceMap文件. */

    /*生成目标文件的sourceMap     sourceMap与 inlineSourceMap二选一*/
    // "sourceMap": true,                           /* 编译时是否生成.map文件 */

    // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中
    // "outFile": "./",                             /* 指定将输出文件合并为一个文件,它的值为一个文件路径名,比如设置为"./dist/main.js",则输出的文件为一个main.js文件。但是要注意,只有设置module的值为amd和system模块时才支持这个配置。 */
    // "outDir": "./",                              /* outDir用来指定输出文件夹,值为一个文件夹路径字符串,输出的文件都将放置在这个文件夹 */

    // 指定输入文件目录(用户输出)
    // "rootDir": "./",                             /* 用来指定编译文件的根目录,编译器会在根目录查找入口文件,如果编译器发现以rootDir的值作为根目录查找入口文件并不会把所有文件加载进去的话会报错,但是不会停止编译。 */
    // "composite": true,                           /* 是否编译构建引用项目 */
    // "tsBuildInfoFile": "./",                     /* Specify file to store incremental compilation information */
    // "removeComments": true,                      /* 是否将编译后的文件中的注释删掉,设为true的话即删掉注释,默认为false。 */
    // "noEmit": true,                              /* 不生成编译后的文件 */
    // "importHelpers": true,                       /* 指定是否引入tslib里的辅助工具函数,默认为false。 */

    /*
        降级遍历器的实现
        如果目标语言是ES3,ES5 , 对遍历器就会有一个较低级的实现
        */
    // "downlevelIteration": true,                  /* 当target为“ES5”或“ES3”时,为“for-of”、“spread”和“destructuring”中的迭代器提供完全支持 */
    // "isolatedModules": true,                     /* 指定是否将每个文件作为单独的模块,默认为true,他不可以和declaration同时设定。 */
    /* Strict Type-Checking Options */
    /*
        开启所有严格的类型检查
        如果 strict=true,则 所有strict相关的都应该为true
        */
    "strict": true /* 用于指定是否启动所有类型检查,如果设为true则会同时开启下面这几个严格类型检查 */,
    // "noImplicitAny": true,                       /* 如果我们没有为一些值设置明确的类型,编译器会默认认为这个值为any类型,如果将noImplicitAny设为true,则如果没有设置明确的类型会报错, */

    // 不允许把null, undefined 赋值给其他类型变量
    // "strictNullChecks": true,                    /* 是否开启非空检查. */

    // 不允许函数参数双向协变
    // "strictFunctionTypes": true,                 /* 是否开启函数类型检查 */
    // "strictBindCallApply": true,                 /* 设为true后会对bind、call和apply绑定的方法的参数的检测是严格检测 */

    // 类的实例属性必须初始化
    // "strictPropertyInitialization": true,        /* 设为true后会检查类的非undefined属性是否已经在构造函数里初始化,如果要开启这项,需要同时开启strictNullChecks */

    // 不允许this有隐式的any类型
    // "noImplicitThis": true,                      /* 当 this表达式的值为 any类型的时候,生成一个错误 */

    // 在代码中 注入 "use strict"
    // "alwaysStrict": true,                        /* 指定始终以严格模式检查每个模块,并且在编译之后的JS文件中加入"use strict"字符串,用来告诉浏览器该JS为严格模式 */
    /* Additional Checks */

    // 检查只声明,未使用的局部变量
    // "noUnusedLocals": true,                      /* 用于检查是否有定义了但是没有使用的变量,对于这一点的检测,使用ESLint可以在你书写代码的时候做提示,你可以配合使用 */

    // 检查未使用的函数参数
    // "noUnusedParameters": true,                  /* 用于检查是否有在函数体中没有使用的参数,这个也可以配合ESLint来做检查 */

    // 每个分支都要有返回值
    // "noImplicitReturns": true,                   /* 用于检查函数是否有返回值,设为true后,如果函数没有返回值则会提示. */

    // 防止 switch语句贯穿:必须存在 break或return
    // "noFallthroughCasesInSwitch": true,          /* 用于检查switch中是否有case没有使用break跳出switch. */
    // "noUncheckedIndexedAccess": true,            /* Include 'undefined' in index signature results */
    // "noPropertyAccessFromIndexSignature": true,  /* Require undeclared properties from index signatures to use element accesses. */
    /* Module Resolution Options */
    // "moduleResolution": "node",                  /* 用于选择模块解析策略,有"node"和"classic"两种类型 */

    // 解析非相对模块的基地址
    // "baseUrl": "./",                             /* 用于设置解析非相对模块名称的基本目录,相对模块不会受baseUrl的影响. */

    /*
        // 路径映射,相对于 baseUrL
        "paths": {
            "jquery":["node_modules/jquery/dist/jquery.slim.min.js"],
        },
        */
    // "paths": {},                                 /* 用于设置模块名到基于baseUrl的路径映射. */

    // 将多个目录放在一个虚拟目录下, 用于运行时
    // "rootDirs": [],                              /* 指定一个路径列表,在构建时编译器会将这个路径列表中的路径中的内容都放到一个文件夹中. */

    /*声明文件目录,默认为  node_modules/@types  */
    // "typeRoots": [],                             /* 用来指定声明文件或文件夹的路径列表,如果指定了此项,则只有在这里列出的声明文件才会被加载。. */

    /*  声明文件包  */
    // "types": [],                                 /* 指定需要包含的模块,只有在这里列出的模块的声明文件才会被加载进来. */
    // "allowSyntheticDefaultImports": true,        /* 用来指定允许从没有默认导出的模块中默认导入. */

    // 允许 export = 导出, 由 import from导入
    "esModuleInterop": true /* 通过为导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性. Implies 'allowSyntheticDefaultImports'. */,
    // "preserveSymlinks": true,                    /* 不把符号链接解析为其真实路径,具体可以了解下webpack和nodejs的symlink相关知识. */

    // 允许在模块中访问umd全局变量
    // "allowUmdGlobalAccess": true,                /* Allow accessing UMD globals from modules. */
    /* Source Map Options */
    // "sourceRoot": "",                            /* 用于指定调试器应该找到TypeScript文件而不是源文件位置,这个值会被写进.map文件里 */
    // "mapRoot": "",                               /* 用于指定调试器找到映射文件而非生成文件的位置,指定map文件的根路径,该选项会影响.map文件中的sources属性。 */

    /*生成目标文件的 inline  sourcemap*/
    // "inlineSourceMap": true,                     /* 指定是否将map文件的内容和js文件编译在一个同一个js文件中,如果设为true,则map的内容会以//# sourceMappingURL=然后接base64字符串的形式插入在js文件底部。 */
    // "inlineSources": true,                       /* 用于指定是否进一步将.ts文件的内容也包含到输出文件中 */
    /* Experimental Options */
    // "experimentalDecorators": true,              /* 用于指定是否启用实验性的装饰器特性 */
    // "emitDecoratorMetadata": true,               /* 用于指定是否为装饰器提供元数据支持,关于元数据,也是ES6的新标准,可以通过Reflect提供的静态方法获取元数据,如果需要使用Reflect的一些方法,需要引入ES2015.Reflect这个库  */
    /* Advanced Options */
    "skipLibCheck": true /* Skip type checking of declaration files. */,
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  }
  // "files": [], // files可以配置一个数组列表,里面包含指定文件的相对或绝对路径,编译器在编译的时候只会编译包含在files中列出的文件。如果不指定,则取决于有没有设置include选项,如果没有include选项,则默认会编译根目录以及所有子目录中的文件。这里列出的路径必须是指定文件,而不是某个文件夹,而且不能使用* ? **/ 等通配符
  // "include": [], // include也可以指定要编译的路径列表,但是和files的区别在于,这里的路径可以是文件夹,也可以是文件,可以使用相对和绝对路径,而且可以使用通配符,比如"./src"即表示要编译src文件夹下的所有文件以及子文件夹的文件。
  // "exclude": [], // exclude表示要排除的、不编译的文件,他也可以指定一个列表,规则和include一样,可以是文件可以是文件夹,可以是相对路径或绝对路径,可以使用通配符。
  // "extends": "", // extends可以通过指定一个其他的tsconfig.json文件路径,来继承这个配置文件里的配置,继承来的文件的配置会覆盖当前文件定义的配置。TS在3.2版本开始,支持继承一个来自Node.js包的tsconfig.json配置文件.
  // "compileOnSave": true // compileOnSave的值是true或false,如果设为true,在我们编辑了项目中文件保存的时候,编辑器会根据tsconfig.json的配置重新生成文件,不过这个要编辑器支持。
  // "references": [] // 一个对象数组,指定要引用的项目
}

实战演练

编译成 amd 模块的代码

image.png

index.ts

import amd = require("./amd");

amd.ts

let amd: string = "1";

export = amd;

tsconfig.json

{
  "compilerOptions": {
    /*生成代码的模块标准*/
    "module": "amd", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    /*将多个相互依赖的文件生成一个文件,可以用在AMD模块中*/
    "outFile": "./app.js",                       /* Concatenate and emit output to single file. */
  }
}

生成后的 app.js

define("amd", ["require", "exports"], function (require, exports) {
  "use strict";
  var amd = "1";
  return amd;
});
define("index", ["require", "exports"], function (require, exports) {
  "use strict";
  exports.__esModule = true;
});

指定 TS 编译后的目录结构

image.png

直接编译到当前目录-不包含 src

{
  // "include": [
  //   "src"
  // ],
  "exclude": ["config"],
  "compilerOptions": {
    /*目标语言的版本*/
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    /*生成代码的模块标准*/
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    /*TS需要引用的库,即声明文件,target为ES5时 默认导入 dom,es5,scripthost*/
    /* Specify library files to be included in the compilation. */
    "lib": [
      "DOM",
      "ES2015",
      "ScriptHost",
      "ES2019"
    ],
    /*允许编译JS文件(js,jsx)*/
    "allowJs": true, /* Allow javascript files to be compiled. */
    /*允许在JS文件中报错,通常与 allowJs 一起使用*/
    "checkJs": true, /* Report errors in .js files. */
    /*指定输出目录*/
    "outDir": "./out", /* Redirect output structure to the directory. */
    /*指定输入文件目录(用户输出)  用户控制目录输出结构 */
    "rootDir": "./src",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

  }
}

编译到当前目录-包含 src

{
  // "include": [
  //   "src"
  // ],
  "exclude": ["config"],
  "compilerOptions": {
    /*目标语言的版本*/
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    /*生成代码的模块标准*/
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    /*TS需要引用的库,即声明文件,target为ES5时 默认导入 dom,es5,scripthost*/
    /* Specify library files to be included in the compilation. */
    "lib": [
      "DOM",
      "ES2015",
      "ScriptHost",
      "ES2019"
    ],
    /*允许编译JS文件(js,jsx)*/
    "allowJs": true, /* Allow javascript files to be compiled. */
    /*允许在JS文件中报错,通常与 allowJs 一起使用*/
    "checkJs": true, /* Report errors in .js files. */
    /*指定输出目录*/
    "outDir": "./out", /* Redirect output structure to the directory. */
    /*指定输入文件目录(用户输出)  用户控制目录输出结构 */
    "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */

  }
}

导入 helper 函数 + 降低遍历器的实现

tsconfig.json

{
  "exclude": ["config"],
  "compilerOptions": {
    /*目标语言的版本*/
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    /*生成代码的模块标准*/
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    /*TS需要引用的库,即声明文件,target为ES5时 默认导入 dom,es5,scripthost*/
    /* Specify library files to be included in the compilation. */
    "lib": [
      "DOM",
      "ES2015",
      "ScriptHost",
      "ES2019"
    ],
    "noEmitHelpers": true,
    "importHelpers": true,

    "downlevelIteration": true,
  }
}

index.ts

// 需要在  tsconfig.json  中 配置   "lib": ["ES2019.Array"],
const a = [[1, 2, 3]].flat();

const doSomething = () => {};

console.log(a);

const arr = [1, 2, 3];

const arr2 = [...arr, 4, 5, 6];

export { doSomething, a };

编译后的 index.js

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.a = exports.doSomething = void 0;
var tslib_1 = require("tslib");
// 需要在  tsconfig.json  中 配置   "lib": ["ES2019.Array"],
var a = [[1, 2, 3]].flat();
exports.a = a;
var doSomething = function () {};
exports.doSomething = doSomething;
console.log(a);
var arr = [1, 2, 3];
var arr2 = tslib_1.__spread(arr, [4, 5, 6]);

代码中 导入了 var tslib_1 = require("tslib"); 工具函数,通过工具函数的 tslib_1.__spread 实现遍历器

对比  "downlevelIteration": false, 后的编译结果

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.a = exports.doSomething = void 0;
var tslib_1 = require("tslib");
// 需要在  tsconfig.json  中 配置   "lib": ["ES2019.Array"],
var a = [[1, 2, 3]].flat();
exports.a = a;
var doSomething = function () {};
exports.doSomething = doSomething;
console.log(a);
var arr = [1, 2, 3];
var arr2 = tslib_1.__spreadArrays(arr, [4, 5, 6]);

代码中 导入了 var tslib_1 = require("tslib"); 工具函数,通过工具函数 tslib_1.__spreadArrays 实现遍历

严格的  bind/call/apply  检查+ 不允许 this 有隐式的 any 类型

tsconfig.json

{
  // "include": [
  //   "src"
  // ],
  "exclude": ["config"],
  "compilerOptions": {
    /*目标语言的版本*/
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    /*生成代码的模块标准*/
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    /*TS需要引用的库,即声明文件,target为ES5时 默认导入 dom,es5,scripthost*/
    /* Specify library files to be included in the compilation. */
    "lib": [
      "DOM",
      "ES2015",
      "ScriptHost",
      "ES2019"
    ],
    "noEmitHelpers": true,
    "importHelpers": true,

    "downlevelIteration": false,

    "strict": true,
      // 严格的 bind/call/apply 检查
      "strictBindCallApply": true,
      // 不允许this有隐式的any类型
      "noImplicitThis": true,
  }
}

index.ts

function add(x: number, y: number) {
  return x + y;
}
// 类型“"2"”的参数不能赋给类型“number”的参数。ts(2345)
add.call(undefined, 1, "2");

class A {
  a: number = 1;
  getA() {
    return function () {
      /*
                "this" 隐式具有类型 "any",因为它没有类型注释。ts(2683)
index.ts(28, 20): 此容器隐藏了 "this" 的外部值。
                */
      console.log(this.a);
    };
  }
}

const myA = new A();

const func = myA.getA();
func();

修改 index.ts

function add(x: number, y: number) {
  return x + y;
}
add.call(undefined, 1, 2);

class A {
  a: number = 1;
  getA() {
    return () => {
      console.log(this.a);
    };
  }
}

const myA = new A();

const func = myA.getA();
func();