-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
关于tsconfig.json,最后更新2023/11月... #23
Comments
Top Level一些根配置项。 extends继承另一个配置文件,推荐的一些官方的配置文件来继承使用:https://github.com/tsconfig/bases/tree/main/bases files, include, exclude这几项指定了在项目里,被ts作用的文件集合。配置本身没什么好说的,记录下它们之间的关系。
references用于项目文件夹分别打包,并且其中有缓存机制的参与,所以编译速度会快很多。 如下项目结构: ./interface
./components
./user
./admin
./tsconfig.json 在上面,user和admin文件夹分别是业务用户端、业务管理端,interface是接口定义,components是通用组件。在以往,改动了任何文件,都需要整个项目重新编译。而在使用references之后,将4个文件夹中放入对应的tsconfig.json并各有配置,在根tsconfig中指定好references的path后,tsc利用缓存机制,会只打包改动过的文件夹。 |
compilerOptions编译器配置项太多了,按作用拆分成几个评论来说。 |
compilerOptions - Type Checking和类型检查有关的配置项。 strictstrict是一个严格模式的快捷开关,开启后会默认打开strictNullChecks、noImplicitAny等选项。并且随着typescript的升级,它可能会默认开启新增特性,列出截至5.2默认开启的选项: alwaysStrict alwaysStrict对所有文件启用javascript strict模式。注意,这和typescript strict不是同一个东西。参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode allowUnreachableCode对未使用的代码的处理策略,可设置警告(默认)、错误、忽略。 allowUnusedLabels对未使用的Label的处理策略,可设置警告(默认)、错误、忽略。 exactOptionalPropertyTypes对可选项设置为undefined的策略。 举例: interface User {
sex?: 1 | 0;
}
const man: User = {};
man.sex = 1;
man.sex = 0;
man.sex = undefined;
noImplicitAny对推导类型为any的策略。 function fn(s) {
console.log(s.subtr(3));
} 入参 noImplicitReturns对函数每个分支必须有return语句的策略。 function fn(s: string) {
if (s) {
return s.substring(3);
} else {
'nothing';
}
} 开启后,ts报错认为 noPropertyAccessFromIndexSignature对访问未定义的对象属性的策略。 interface User {
name?: string;
[key: string]: string;
}
const a: User = {};
console.log(a.name, a['name'], a.sex, a['sex']); 访问对象属性有 noUncheckedIndexedAccess未定义的对象属性值类型,给追加上 interface User {
name: string;
[propName: string]: string;
}
declare const admin: User;
// (property) User.name: string
console.log(admin.name);
// (index) User[string]: string
console.log(admin.birthday); 上例中,birthday的值类型是 // (index) User[string]: string | undefined
console.log(admin.birthday); noUnusedLocals局部变量未使用,抛出错误。 function test() {
const defaultModelID = 23;
// 'defaultModelID' is declared but its value is never read.
} noUnusedParameters入参未使用,抛出错误。 const createDefaultKeyboard = (modelID: number) => {
// 'modelID' is declared but its value is never read.
const defaultModelID = 23;
return { type: "keyboard", modelID: defaultModelID };
}; strictBindCallApply在使用函数的call、bind、apply时,是否要检查传入参数的类型。 function fn(x: string) {
return x;
}
const n1 = fn.call(undefined, '10');
const n2 = fn.call(undefined, false); 如上代码中,第二个call中的入参false和string类型不匹配而报错。 strictFunctionTypes更精确的检查函数入参类型。 type GetUser = (id: string | number) => any;
function test(x: string) {
return x;
}
const getUser: GetUser = test; 如上代码中,GetUser的入参类型 strictNullChecks是否校验 在以往的纯js项目中,容易忽略变量为undefined后仍然访问其属性的场景,比如在如下代码中,忽略了 const a = arr1.find();
console.log(a.name) 本选项开启后,在静态检查时就提示开发者,变量可能是 useUnknownInCatchVariables有时候我们并不知道catch的err类型是什么,它的类型由try里的实际运行分支决定,而如果当成any处理,那么访问它的属性是危险的。当开启本项后,err的类型是unknown,必须先限定其类型再安全的访问其属性,如: try {
// ...
} catch (err) {
if (err instanceof Error) {
console.log(err.message);
}
} |
compilerOptions - Modules模块的处理策略。 allowArbitraryExtensionsts默认支持了ts、js、cjs、jsx等模块的解析描述,通过 如: // test.ts
// 报错:Cannot find module './a.jpk' or its corresponding type declarations.ts(2307)
import a from './a.jpk';
// doTest类型是any
console.log(a.doTest()); 开启该项,并增加 // a.d.jpk.ts
declare const jpk: {
doTest: () => void;
};
export default jpk;
// test.ts
import a from './a.jpk';
// doTest的类型:(property) doTest: () => void
console.log(a.doTest()); allowImportingTsExtensions是否允许在import path带入ts、tsx等后缀名。 ts项目需要编译成js代码后执行,如果我们使用ts-node来执行项目,可以启用 举例: import { wait } from '@/utils/utils.ts'; allowUmdGlobalAccess// TODO baseUrl为解析无路径修饰的模块,设置一个基础路径。 什么叫做无路径修饰?就是该模块不是一个绝对或相对路径,如 如果配置了该项,那么ts从该项指定的目录中开始查找模块,优先级也高于 module模块打包的策略,也就是指编译后的模块加载代码是require、import之类的语法。推荐大家阅读:模块理论。 这个话题有点繁琐,也涉及到javascript的模块化amd、umd之类的历史,对于2023年这个时间点来说,关注下CommonJS、ESM即可,ESM还细分为ES2015、ES20XX等版本,他们的模块打包策略是一样的,区别是更高版本的ESM还支持了 补充的是,nodejs的模块打包策略也早就支持esm了,具体参照package.json的设置:https://www.typescriptlang.org/docs/handbook/modules/reference.html#node16-nodenext moduleResolution加载模块的策略,也就是指用什么策略去解析
这东西还要和module搭配使用,并且!并且!和package.json还有扯不清的关系。强烈建议阅读上文提到的模块理论 另外,由于自己对这块的理解也不足深,还参考了这篇文章:https://zhuanlan.zhihu.com/p/621795173。 moduleSuffixes模块搜索时的文件扩展名补充,比如以下配置项: {
"compilerOptions": {
"moduleSuffixes": [".ios", ".native", ""]
}
} import * as foo from "./foo"; TypeScript 会按顺序寻找 paths在加载模块路径时,加入一个别名匹配。可以理解为webpack的alias。 resolveJsonModule允许加载json模块(文件)。 resolvePackageJsonExports引用 resolvePackageJsonImports强制typescript使用package.json中imports字段的定义去加载 // package.json
"imports": {
"#test/*": "./src/test/*"
} // src/test/1.ts
export const a = 123; // src/b.ts
import { a } from '#test/1';
console.log('==a', a); rootDir指定项目的根目录,默认值可以理解为所有ts文件目录的最大集。例如: MyProj
├── tsconfig.json
├── core
│ ├── a.ts
│ ├── b.ts
│ ├── sub
│ │ ├── c.ts
├── types.d.ts 如上的项目,rootDir的推断值是"core"。但是你可以手工指定为 另外,如果设置了 rootDirs这个属性有点拽,它可以将多个目录虚拟成一个目录。这样在 typeRoots类型定义文件的目录,默认所有@types目录都会被包含,包括node_modules下的@types目录。 types类型定义文件的具体目录,规则和 |
compilerOptions - Emit编译策略。 outDir编译文件放置的目录,默认和源码放同一个目录。 noEmit不要编译ts。 项目中一般使用babel去编译,typescript仅用来做静态检查,所以设置noEmit就可以不生成js、sourcemap、declaration文件了。 noEmitOnError顾名思义,在检查到错误的时候,不要继续编译了。 declaration / emitDeclarationOnly / declarationDir生成类型描述文件。 这个是typescript最重要的几个特性之一了,为 同时,可以使用 declarationMap给 sourceMap / inlineSourceMap / inlineSources / mapRoot / sourceRoottypescript也是支持souce map的,这里就不解释sourcemap是什么了。它的相关配置项有: sourceMap,生成sourcemap文件。 downlevelIteration对迭代器的降级解析。 在es6中增加了for/of,spread等迭代特性,typescript在编译成es5的时候,要使用何种语法。文档中有个字符遍历的例子说明开启与否的迭代影响,这对业务计算结果是有影响的。 不过项目中使用了babel之类的编译器,就不用担心这些影响了。 importHelpers引入降级解析的包。 将es6的某些特新编译成es5,如迭代器、异步语法等,会将模块中每个相应的代码都编译成降级代码,使得类似代码重复。如: var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
export function fn(arr) {
var arr2 = __spreadArray([1], __read(arr), false);
} 如果同时开启了 import { __read, __spreadArray } from "tslib";
export function fn(arr) {
var arr2 = __spreadArray([1], __read(arr), false);
} noEmitHelperstypescript处理迭代器、异步等的降级策略是生成一堆降级代码,也可以使用"importHelpers"和"tslib"来提取公共代码。但通过这个配置,可以自定义降级代码了。具体参照文档。 preserveConstEnums是否在编辑结果中移除 const enum Album {
JimmyEatWorldFutures = 1,
TubRingZooHypothesis = 2,
DogFashionDiscoAdultery = 3,
}
const selectedAlbum = Album.JimmyEatWorldFutures;
if (selectedAlbum === Album.JimmyEatWorldFutures) {
console.log("That is a great choice.");
} 编译结果如下: "use strict";
const selectedAlbum = 1 /* Album.JimmyEatWorldFutures */;
if (selectedAlbum === 1 /* Album.JimmyEatWorldFutures */) {
console.log("That is a great choice.");
} 如果开启此项,则编译结果中会描述出该enum的结构。如下: "use strict";
var Album;
(function (Album) {
Album[Album["JimmyEatWorldFutures"] = 1] = "JimmyEatWorldFutures";
Album[Album["TubRingZooHypothesis"] = 2] = "TubRingZooHypothesis";
Album[Album["DogFashionDiscoAdultery"] = 3] = "DogFashionDiscoAdultery";
})(Album || (Album = {}));
const selectedAlbum = 1 /* Album.JimmyEatWorldFutures */;
if (selectedAlbum === 1 /* Album.JimmyEatWorldFutures */) {
console.log("That is a great choice.");
} preserveValueImports通常,打包器会将未使用的"import"移除掉,但开启了该特性后,会在编译中保留未使用的"import"。例如: import { Animal } from "./animal.js";
eval("console.log(new Animal().isDangerous())"); removeComments移除代码中的注释。 |
compilerOptions - JavaScript Support对javascript文件的支持 allowJs允许 checkJs是否开启对 maxNodeModuleJsDepth为 正常情况下,我们会在 |
compilerOptions - Interop Constraints(模块)互相操作的约束 allowSyntheticDefaultImports允许合成 如果一个模块A没有 有一个做法是 esModuleInteropesm在使用 forceConsistentCasingInFileNames强制引用的模块文件名,和磁盘中的文件名保持一致的大小写。 |
compilerOptions - Language and Environment和语言、环境有关的配置项。 jsx如何解析 lib在项目中使用的api类型定义集合。 ts是需要知道你用的每一个javascript方法、属性的定义的,所以它内置了一批定义(比如Math.abs方法)。但是呢,后面新增方法的定义,需要你手工指定包含,ts才能理解了,比如Array.include方法,你就要包含ES2016。但好在你不需要记住这些,因为当你指定 另外,大部分情况下,我们要访问一些和特定环境有关的特性,比如浏览器里的Dom特性,那么这儿额外引入 target项目编译的目标javascript版本。 也就是你的目标客户端支持的最低javascript版本。注意它会影响默认的 |
compilerOptions - Compiler Diagnostics编译诊断配置项。 diagnostics / extendedDiagnostics打印出编译的信息,比如多少文件、编译时间等。 extendedDiagnostics包含了diagnostics给出的所有信息,所以用extendedDiagnostics就行了。这是一个开启extendedDiagnostics的编译信息: Files: 143
Lines of Library: 38663
Lines of Definitions: 82578
Symbols: 73362
Types: 1495
Parse time: 0.50s
ResolveModule time: 0.03s
Total time: 1.01s explainFiles打印出文件被编译的原因,也就是它们的引用链。 listEmittedFiles / listFiles列出参与编译的文件。 traceResolution打印每一个文件的编译流程。 |
compilerOptions - Completeness完整性检测的配置项 skipLibCheck跳过lib库的类型检测。 |
截至TypeScript 5.2,
tsconfig.json
的配置项已经有百十个之多,它的某些选项甚至影响了项目的执行结果,所以尽量多的了解它们能让程序员更深的了解ts,写出优美语句让CTO赞赏,甚至避免写出bug。本issue的内容,只是个人在工作经验的影响下,对官方文档的内容理解。且内容有所欠缺,基本只记录了我工作涉及到的配置项,比如不怎么使用class,对class的配置项就略过了。
官方文档:https://www.typescriptlang.org/tsconfig
The text was updated successfully, but these errors were encountered: