原文:https://github.com/microsoft/...

豆皮粉儿们,又又又见面了,明天这一期,由字节跳动数据平台的“StoneyAllen
”,给大家翻译一篇文章“typescript性能”。在2020年倒计时最初几天我要祝大家新年快乐~,打工人,打工魂~。上面就开始仔细阅读吧!

翻译者:StoneyAllen

有些简略的Typescript配置,能够让你取得更快的编译和编辑体验,这些办法越早把握越好。上面列举了除了最佳实际以外,还有一些用于考察迟缓的编译/编辑体验的罕用技术,以及一些作为最初伎俩来帮忙TypeScript团队考察问题的罕用办法。

编写易编译代码

优先应用接口而不是穿插类型

很多时候,简略对象类型的类型别名与接口的作用十分类似

然而,只有你须要定义两个及以上的类型,你就能够选用接口来扩大这些类型,或者在类型别名中对它们相交,这时差别就变得显著了。

因为接口定义的是繁多立体对象类型,能够检测属性是否抵触,解决这些抵触是十分必要的。另一方面,穿插类型只是递归的合并属性,有些状况下会产生never。接口则体现的一贯很好,而穿插类型定义的类型别名不能显示在其余的穿插类型上。接口之间的类型关系也会被缓存,而不是整个穿插类型。最初值得注意的区别是,如果是穿插类型,会在查看“无效” /“展平”类型之前查看所有属性。

因而,倡议在创立穿插类型时应用带有接口/扩大的扩大类型。

应用类型正文

增加类型正文,尤其是返回类型,能够节俭编译器的大量工作。这是因为命名类型比匿名类型更简洁(编译器更喜爱),这缩小了大量的读写申明文件的工夫。尽管类型推导是十分不便的,没有必要到处这么做。然而,如果您晓得了代码的慢速局部,可能会很有用。

优先应用根底类型而不是联结类型

联结类型十分好用--它能够让你表白一种类型的可能值范畴。

然而他们也带来了肯定开销。每次将参数传递给 printSchedule 时,须要比拟联结类型里的每个元素。对于一个由两个元素组成的联结类型来说,这是微不足道的。然而,如果你的联结类型有很多元素,这将引起编译速度的问题。例如,从联结类型中淘汰多余的局部,元素须要成对的去比拟,工作量是呈二次递增的。当大量联结类型穿插一起时产生这种查看,会在每个联结类型上相交导致大量的类型,须要缩小这种状况产生。防止这种状况的一种办法是应用子类型,而不是联结类型。

一个更事实的例子是,定义每种内置DOM元素的类型时。这种状况下,更优雅的形式是创立一个蕴含所有元素的 HtmlElement 根底类型,其中包含 DivElementImgElement 等。应用继承而不是创立一个无穷多的联结类型 DivElement | /*...*/ | ImgElement | /*...*/

应用我的项目援用

应用TypeScript构建内容较多的代码时,将代码库组织成几个独立的我的项目会很有用。每个我的项目都有本人的 tsconfig.json ,可能它会对其余我的项目有依赖性。这有益于防止在一次编译中导入太多文件,也使某些代码库布局策略更容易地放在一起。

有一些十分根本的办法将一个代码库分解成多个我的项目。举个例子,一个程序代码,一部分用作客户端,一部分用作服务端,另一部分被其它两个共享

测试也能够合成到本人的我的项目中

一个常见的问题是 "一个我的项目应该有多大?"。这很像问 "一个函数应该有多大?"或 "一个类应该有多大?",在很大水平上,这归纳于教训。人们相熟的一种宰割JS/TS代码的办法是应用文件夹。作为一种启发式的办法,如果它们关联性足够大,能够放在同一个文件夹中,那么它们就属于同一个我的项目。除此之外,要避免出现极大或极小规模的我的项目。如果一个我的项目比其余所有我的项目加起来都要大,那就是一个正告信号。同样,最好防止有几十个单文件我的项目,因为也会减少开销。

你能够在这里浏览更多对于我的项目参考资料

配置tsconfig.json或jsconfig.json

TypeScriptJavaScript用户能够用tsconfig.json文件任意配置编译形式。JavaScript用户也能够应用jsconfig.json文件配置本人的编辑体验。

指定文件

你应该始终确保你的配置文件没有蕴含太多文件

tsconfig.json 中,有两种形式能够指定我的项目中的文件

  • files列表
  • include、exclude列表

两者的次要区别是,files冀望失去一个源文件的文件门路列表,而include/exclude应用通配符模式对文件进行匹配

尽管指定文件能够让TypeScript间接疾速地加载文件,但如果你的我的项目中有很多文件,而不只是几个顶层的入口,那就会很麻烦。此外,很容易遗记增加新文件到tsconfig.json中,这意味着你可能最终会失去奇怪的编辑器行为,这些新文件被谬误地剖析,这些都很辣手。

include/exclude有助于防止指定这些文件,但代价是:必须通过include蕴含的目录来发现文件。当运行大量的文件夹时,这可能会减慢编译速度。此外,有时编译会蕴含很多不必要的.d.ts文件和测试文件,这会减少编译工夫和内存开销。最初,尽管exclude有一些正当的默认值,但某些配置比方mono-repos,意味着像node_modules这样的 "重 "文件夹依然能够最终被蕴含。

对于最佳做法,咱们倡议如下:

  • 在您的我的项目中只指定输出文件夹(即您想将其源代码蕴含在编译/剖析中的文件夹)
  • 不要把其余我的项目的源文件混在同一个文件夹里
  • 如果把测试和其余源文件放在同一个文件夹里,请给它们取一个不同的名字,这样就能够很容易地把它们排除在外
  • 防止在源目录中呈现大的构建工件和依赖文件夹,如node_modules

留神:如果没有排除列表,默认状况下node_modules是被排除的;一旦增加了node_modules,就必须明确地将node_modules增加到列表中。

上面是一个正当的tsconfig.json,用来演示这个操作

管制蕴含的@types

默认状况下,TypeScript会主动蕴含每一个在node_modules文件夹中找到的@types包,不论你是否导入它。这是为了在应用Node.js、Jasmine、Mocha、Chai等工具/包时,使某些货色 "可能工作",因为这些工具/包没有被导入--它们只是被加载到全局环境中

有时这种逻辑在编译和编辑场景下都会拖慢程序的构建工夫,甚至会造成多个全局包的申明抵触的问题,造成相似于如下问题

在不须要全局包的状况下,修复办法很简略,只有在 tsconfig.json/jsconfig.json 中为 "type "选项指定一个空字段即可。

如果您依然须要一些全局包,请将它们增加到类型字段中

增量我的项目输入

--incremental标记容许TypeScript将上次编译的状态保留到一个 .tsbuildinfo 文件中。这个文件用来计算上次运行后可能被从新查看/从新输入的最小文件集,就像TypeScript的--watch模式一样。

当对我的项目援用应用复合标记时,默认状况下会启用增量编译,但这样也能带来同样的速度晋升。

跳过 .d.ts 查看

默认状况下,TypeScript会对一个我的项目中的所有.d.ts文件进行全面查看,以发现问题或不统一的中央;然而,这查看通常是不必要的。大多数时候,.d.ts文件都是已知如何工作的--类型之间互相扩大的形式曾经被验证过一次,重要的申明还是会被查看。

TypeScript提供了一个选项,应用skipDefaultLibCheck标记来跳过.d.ts文件的类型查看(例如lib.d.ts)

另外,你也能够启用 skipLibCheck 标记来跳过编译中的所有 .d.ts 文件

这两个选项通常会暗藏.d.ts文件中的谬误配置和抵触,所以只倡议在疾速构建场景中应用它们。

应用更快的差别查看

狗的列表是动物的列表吗?也就是说,List<Dog>是否能够调配给List<Animals>?寻找答案的间接办法是一一成员进行类型构造比拟。可怜的是,这可能带来低廉的性能开销。然而,如果咱们对List<T>有足够的理解,咱们能够将这个可调配性查看简化为确定Dog,是否能够调配给Animal(即不思考List<T>的每个成员)。特地是,当咱们须要晓得类型参数T的差异。编译器只有在启用strictFunctionTypes标记的状况下,能力充分利用这种潜在的减速劣势(否则,它就会应用较慢的,但更宽松的构造查看)。因而,咱们倡议应用 --strictFunctionTypes 来构建(默认在 --strict 下启用)

配置其余构建工具

TypeScript编译常常与其余构建工具一起执行--特地是在编写可能波及捆绑程序的Web应用程序时。尽管咱们只能对一些构建工具提出倡议,但现实状况下,这些技术能够被遍及。

确保除了浏览本节外,你还浏览了对于你所抉择的构建工具的性能--例如:

  • ts-loader的Faster Builds局部
  • awesome-typescript-loader的性能问题局部

并行类型查看

类型查看通常须要从其余文件中获取信息,与转换/输入代码等其余步骤相比,类型查看可能绝对低廉。因为类型查看可能会破费更多的工夫,它可能会影响到外部的开发循环--换句话说,你可能会经验更长的编辑/编译/运行周期,这可能会令你头疼。

出于这个起因,一些构建工具能够在一个独自的过程中运行类型查看,而不会阻塞输入。尽管这意味着在TypeScript构建而产生错误报告之前曾经有有效的代码运行,通常会先在编辑器中看到谬误,而不会被长时间地阻止运行工作代码

一个理论的例子是Webpack的fork-ts-checker-webpack-plugin插件,或者awesome-typescript-loader有时也会这样做。

隔离文件输入

默认状况下,TypeScript输入须要的语义信息可能不是本地文件。这是为了了解如何输入像 const enumsnamespaces 这样的性能。然而须要查看其余文件来生成某个文件,这会使输入速度变慢。

对须要非本地信息的性能需要是比拟少见的--惯例枚举能够用来代替const枚举,模块能够用来代替命名空间。鉴于此,TypeScript提供了isolatedModules标记,以便在由非本地信息驱动的性能上报错。启用 isolatedModules 意味着你的代码库对于应用 TypeScript APIs(如 transpileModule)或代替编译器(如 Babel)的工具是平安的。

举个例子,上面的代码在运行时无奈失常应用独立的文件转换,因为const enum值被冀望内联;侥幸的是, isolatedModules会在晚期通知咱们这一点

记住:isolatedModules不会主动让代码生成速度更快--它只是通知你,你行将应用一个可能不被反对的性能。你要的是独立模块在不同的构建工具和API中的输入

能够通过应用以下工具来影响独立文件的输入

  • ts-loader提供了一个transpileOnly标记,通过应用transpileModule来执行独立文件输入
  • awesome-typescript-loader提供了一个transpileOnly标记,通过应用transpileModule来执行独立文件输入
  • TypeScript能够间接应用transpileModule API
  • awesome-typescript-loader提供了useBabel标记
  • babel-loader以独自的形式编译文件(但不提供类型查看)
  • gulp-typescript 启用 isolatedModules 时,能够实现独立文件输入
  • rollup-plugin-typescript只执行独立文件编译
  • ts-jest能够应用( isolatedModules标记设为true )isolatedModules为true
  • ts-node 能够检测 tsconfig.json 的 "ts-node "字段中的 "transpileOnly "选项,也有一个 --transpile-only 标记。

考察问题

有肯定的办法能够失去可能出问题的提醒

禁用编辑器插件

编辑器的体验受到插件的影响。尝试禁用插件(尤其是JavaScript/TypeScript相干的插件),看看是否能解决性能和响应速度方面的问题。

某些编辑器也有本人的性能故障排除指南,所以能够思考浏览一下。例如,Visual Studio Code也有本人的性能问题介绍。

诊断扩大

你能够用--extendedDiagnostics来运行TypeScript,以取得编译器破费工夫的打印日志。

请留神,总工夫不是后面所有工夫的总和,因为有一些重叠,有些工作是没有掂量工具的。

对于大多数用户来说,最相干的信息是:

思考到这些投入,你可能会想问一些问题:

  • 文件数/代码行数是否与您我的项目中的文件数大抵统一?如果不合乎,请尝试运行--listFiles
  • 程序工夫或I/O读取工夫是否相当高?请确保你的include/exclude配置正确
  • 其余工夫看起来不对劲吗?你可能想提出一个问题。你能够做以下事件来帮忙诊断

    • 如果打印工夫较高,则应用emitDeclarationOnly运行
    • 浏览对于报告编译器性能问题的阐明

显示配置

当运行 tsc 时,并不能显著地看到编译的内容设置,特地是思考到 tsconfig.jsons 能够扩大其余配置文件。showConfig 能够解释 tsc 将为一个调用计算着什么。

追踪分辨率

运行 traceResolution 能够有助于解释,一个文件为什么被蕴含在编译中。输入有点繁琐,所以你可能想把输入重定向到一个文件。

如果你发现了一个不应该存在的文件,你可能须要批改你的tsconfig.json中的include/exclude列表,或者,你可能须要调整其余设置,比方type、typeRoots或paths。

独立运行tsc

很多时候,用户在应用第三方构建工具(如Gulp、Rollup、Webpack等)时都会遇到性能迟缓的问题。运行tsc --extendedDiagnostics,能够发现TypeScript和工具之间的差别,用以阐明内部配置的谬误或效率低下。

一些须要留神的问题:

  • tsc和集成了TypeScript的构建工具在构建工夫上有很大的区别吗?
  • 如果构建工具提供诊断,那么TypeScript的分辨率和构建工具的分辨率是否有区别?
  • 构建工具是否有本人的配置,可能的起因是什么?
  • 构建工具是否有可能是TypeScript集成的配置起因?(例如ts-loader的选项?)

降级依赖性

有时TypeScript的类型查看会受到计算密集的.d.ts文件的影响。这很常见也很可能会产生。降级到一个较新的TypeScript版本(能够更有效率)或一个较新版本的@types包(可能曾经复原了一个回归)通常能够解决这个问题。

常见的问题

一旦你曾经排除了故障,你可能想摸索一些常见问题的修复办法。如果以下解决方案不起作用,可能值得提出问题。

include和exclude配置不当

如上所述,include/exclude选项能够在以下几个方面被滥用

提出问题

如果你的我的项目曾经进行了正确的优化配置,你可能须要提出一个问题。

最好的性能问题报告蕴含容易取得的和最小的问题复制品。换句话说,一个容易通过git克隆的代码库,只蕴含几个文件。它们不须要与构建工具的内部集成--它们能够通过调用tsc或调用TypeScript API的独立代码。不优先思考那些须要简单调用和设置的代码库。

咱们了解这一点却不容易实现--特地是,很难在代码库中隔离问题的源头,而且共享知识产权可能也是一个问题。在某些状况下,如果咱们认为问题影响较大,团队将违心发送一份窃密协定(NDA)。

无论是否能够复制,在提交问题时,依照这些办法,将有助于为您提供性能修复。

报告编译器性能问题

有时,你会在构建工夫以及编辑场景中发现性能问题。在这种状况下,最好关注于TypeScript编译器。

首先,应该应用TypeScript的next版本,以确保你不会碰到那些已解决的问题。

一个编译器的问题可能包含

  • 装置的TypeScript版本(例如:npx tsc -v 或 yarn tsc -v)
  • TypeScript运行的Node版本(例如:node -v)
  • 应用extendedDiagnostics运行的输入(tsc --extendedDiagnostics -p tsconfig.json)
  • 现实的状况是,一个我的项目可能展现所遇到的问题
  • 分析编译器的输入日志(isolate-*-*-*.log 和*.cpuprofile 文件)

分析编译器

通过应用--trace-ic标记与--generateCpuProfile标记,来让TypeScript运行Node.js v10+,这对团队提供诊断后果来说是很重要的:

这里的 ./node_modules/typescript/lib/tsc.js 能够用来替换你的TypeScript编译器的装置版本,而tsconfig.json能够是任何TypeScript配置文件。 profile.cpuprofile是你抉择的输入文件。

这将产生两个文件:

  • --trace-ic 将输入到 isolate-*-*-*.log 的文件中(例如 isolate-00000176DB2DF130-17676-v8.log)
  • --generateCpuProfile将以您抉择的名称输入到一个文件中。在下面的例子中,它将是一个名为 profile.cpuprofile 的文件

正告:这些文件可能蕴含你的工作空间的信息,包含文件门路和源代码。这两个文件都能够作为纯文本浏览,您能够在将它们提交为 GitHub 问题之前批改它们。(例如,革除可能裸露外部专用信息的文件门路)。

然而,如果你对在GitHub上公开公布这些有任何顾虑,请通知咱们,能够私下分享细节。

报告编辑绩效问题

编辑性能常常受到很多货色的影响,TypeScript团队惟一能管制的是JavaScript/TypeScript语言服务的性能,以及该语言服务和某些编辑器(即Visual Studio、Visual Studio Code、Visual Studio for Mac和Sublime Text)之间的集成。确保所有第三方插件在编辑器中被敞开,以确定是否有TypeScript自身的问题。

编辑性能问题稍有波及,但同样的想法也实用于:可被克隆的最小重现代码库是现实的,尽管在某些状况下,团队将可能签订NDA来考察和隔离问题。

包含tsc--extendedDiagnostics的输入是很好的上下文,但取一个TSServer日志是最有用的。

收集TSServer日志

在Visual Studio代码中收集TSServer日志

  1. 关上你的命令菜单栏,而后抉择

    1. 进入 "首选项 "关上您的全局设置。关上用户设置
    2. 入偏好设置,关上本地我的项目。关上工作区设置
  1. 设置选项 "typecript.tsserver.log":"verbose"
  1. 重启VS Code,重现问题
  1. 在VS Code中,运行TypeScript。关上TS服务器日志命令
  1. 这将关上tsserver.log文件

⚠正告:TSServer日志可能会蕴含你的工作空间的信息,包含文件门路和源代码。如果你对在GitHub上公开公布有任何顾虑,请通知咱们,你能够私下分享细节。

The End