Performance TypeScript的提示

2020-11-24 23:58:21

配置TypeScript的方法很简单,以确保更快的编译和编辑体验。采用这些实践的越早越好。除最佳实践之外,还有一些调查缓慢的编译/编辑体验的通用技术,一些常见的修复程序以及一些协助TypeScript团队调查问题的常用方法。

在大多数情况下,对象类型的简单类型别名的行为与接口非常相似。

但是,接口通常在类型显示中具有更好的属性,并且一旦需要组合两种类型,接口就会创建一个单一的平面对象类型来检测属性冲突,这与交集类型相反,交集类型在检查每个组成部分之前先进行检查与交集类型相反,接口之间的类型关系也将被缓存。

-输入Foo = Bar&Baz和{-someProp:string; -} +接口Foo扩展了Bar,Baz {+ someProp:string; +}

添加类型注释,特别是返回类型可以节省编译器大量的工作,部分原因是命名类型比匿名类型(编译器可能会推断出)更紧凑,从而减少了花在阅读和阅读上的时间。编写声明文件(例如,用于增量构建)。类型推断非常方便,因此不需要通用进行此操作-但是,如果您确定了代码的慢速部分,则可以尝试这样做。

-从“其他”导入{otherFunc}; +从“其他”导入{otherFunc,otherType}; -导出函数func(){+导出函数func():otherType {return otherFunc(); }

联合类型很棒-它们使您可以表达类型的可能值的范围。

界面WeekdaySchedule {day:“ Monday” | “星期二” | “星期三” | “星期四” | “星期五”;唤醒:时间; startWork:时间; endWork:时间;睡觉时间 ; }接口WeekendSchedule {day:“ Saturday” | “周日”;唤醒:时间; familyMeal:时间;睡觉时间 ; }声明函数printSchedule(进度:WeekdaySchedule | WeekendSchedule);

但是,它们也要付出代价,每次将参数传递给printSchedule时,都必须将其与并集的每个元素进行比较。对于两元素联合,这是微不足道且便宜的,但是,如果您的并集有更多多于十个元素,可能会导致编译速度上的实际问题。例如,要从某个联合中消除冗余成员,必须成对比较这些元素,这是二次的。当与大型联合相交时,这种检查可能会发生每个工会成员上的过度使用可能会导致需要减少大量类型。避免这种情况的一种方法是使用子类型,而不是工会。

界面Schedule {day:“ Monday” | “星期二” | “星期三” | “星期四” | “星期五” | “星期六” | “周日”;唤醒:时间;睡觉时间 ; }接口WeekdaySchedule扩展了Schedule {day:“ Monday” | “星期二” | “星期三” | “星期四” | “星期五”; startWork:时间; endWork:时间; }接口WeekendSchedule扩展了Schedule {day:“ Saturday” | “周日”; familyMeal:时间; }声明函数printSchedule(schedule:Schedule);

尝试对每种内置DOM元素类型进行建模时,可能会出现一个更现实的示例。在这种情况下,最好创建一个具有常见成员的基本HtmlElement类型,该类型由DivElement,ImgElement等扩展,而不是创建详尽的联盟DivElement | /*...*/ | ImgElement | /*...*/。

当使用TypeScript构建任何大小不小的代码库时,将代码库组织到几个独立的项目中会很有帮助。每个项目都有自己的tsconfig.json文件,该文件依赖于其他项目,这有助于避免加载过多项目文件在一次编译中,并且还使某些代码库布局策略更易于组合在一起。

将代码库分解为项目有一些非常基本的方法,例如,一个程序可能包含一个客户端项目,一个服务器项目以及一个在两者之间共享的项目。

------------ | | |共享| ^ ---------- ^ / \ / \ ------------ ------------ | | | ||客户| |服务器| ----- ^ ------ ------ ^ -----

------------ | | |共享| ^ ----- ^ ---- ^ / | \ / | \ ------------ ------------ ------------ || | |共享| | ||客户| |测试|服务器| ----- ^ ------ ------------ ------ ^ ----- | | | | ------------ ------------ ||客户| |服务器||测试|测试| ------------ ------------

一个常见的问题是“一个项目应该有多大?”,这很像问“一个功能应该有多大?”。或“一个班级应该有多大?”文件夹中的JS / TS代码拆分是一种熟悉的方法,作为一种启发式方法,如果事物相关性足以放入同一文件夹中,则它们属于同一项目。除此之外,避免大型或小型项目。如果一个项目大于所有其他项目的总和,那是一个警告信号。类似地,最好避免拥有多个单文件项目,因为这会增加开销。

TypeScript和JavaScript用户始终可以使用tsconfig.json文件配置其编译。 jsconfig.json文件还可以用于配置JavaScript用户的编辑体验。

您应该始终确保配置文件一次不包含太多文件。

两者之间的主要区别在于,文件需要源文件的文件路径列表,并且包含/排除使用通配符模式来与文件匹配。

虽然指定文件将允许TypeScript快速直接直接加载文件,但是如果您的项目中有很多文件而没有几个顶级入口点,则可能会很麻烦。此外,很容易忘记将新文件添加到tsconfig中.json,这意味着您可能会以奇怪的编辑器行为结束,从而错误地分析了这些新文件。所有这些都可能很麻烦。

include / exclude有助于避免指定这些文件,但是要付出一定的代价:必须通过遍历包含的目录来发现文件。当运行许多文件夹时,这会减慢编译速度。此外,有时编译会包含很多不必要的内容.d.ts文件和测试文件,这可能会增加编译时间和内存开销。最后,尽管exclude具有一些合理的默认值,但某些配置(如mono-repos)意味着仍然可以包含“笨重”的文件夹(如node_modules)。

仅指定项目中的输入文件夹(即,要包含其源代码以进行编译/分析的文件夹)。

如果将测试与其他源文件放在同一文件夹中,请给它们起一个不同的名称,以便可以轻松将它们排除在外。

注意:没有排除列表时,默认情况下将排除node_modules;添加一个时,将node_modules显式添加到列表中很重要。

默认情况下,TypeScript会自动包含在node_modules文件夹中找到的每个@types包,无论您是否导入它。这都是为了在使用Node.js,Jasmine,Mocha,Chai等时使某些事情“正常工作”。因为这些工具/软件包没有被导入-它们只是被加载到全局环境中。

有时,这种逻辑可能会减慢编译和编辑场景中的程序构建时间,甚至可能导致多个全局包的问题与声明冲突,从而导致诸如

如果不需要全局包,则修复就像在tsconfig.json / jsconfig.json中为“ types”选项指定一个空字段一样容易

// src / tsconfig.json {“ compilerOptions”:{// ... //不要自动包含任何内容。 //仅包含我们需要导入的`@ types`软件包。 “ types”:[]},“ files”:[“ foo.ts”]}

// tests / tsconfig.json {“ compilerOptions”:{// ... //仅包含`@ types / node`和`@ types / mocha`。 “ types”:[“ node”,“ mocha”]},“ files”:[“ foo.test.ts”]}

--incremental标志允许TypeScript将上次编译的状态保存到.tsbuildinfo文件中。此文件用于确定自上次运行以来可能要重新检查/重新发射的最小文件集。 TypeScript的--watch模式如何工作。

在将Composite标志用于项目引用时,默认情况下启用增量编译,但是可以为选择加入的任何项目带来相同的加速。

默认情况下,TypeScript对项目中的所有.d.ts文件进行完全重新检查,以发现问题和不一致之处;但是,通常这是不必要的。在大多数情况下,.d.ts文件已经可以工作-类型彼此之间的扩展方式已经过一次验证,并且无论如何都要检查声明。

打字稿提供跳过.d.ts文件,它附带(例如lib.d.ts)使用skipDefaultLibCheck标志的类型检查的选项。

另外,您还可以启用skipLibCheck标志来跳过检查编译中的所有.d.ts文件。

这两个选项通常可以隐藏.d.ts文件中的错误配置和冲突,因此我们建议仅将它们用于更快的构建。

狗列表是动物列表吗?即List 是否可分配给List 吗?找出方法的直接方法是逐个成员对类型进行结构比较。非常昂贵。但是,如果我们对List 的了解足够多,则可以减少此可分配性检查,以确定Dog是否可分配给Animal(即,无需考虑List 的每个成员)。(尤其是,我们需要知道如果启用了strictFunctionTypes标志(仅使用较慢但更宽松的结构检查),则编译器仅可以充分利用这种潜在的加速性能(因此,我们建议使用--strictFunctionTypes(默认在--strict下启用)。

TypeScript编译通常是在考虑其他构建工具的情况下进行的-尤其是在编写可能涉及捆绑程序的Web应用程序时。尽管我们只能对一些构建工具提出建议,但理想情况下可以将这些技术加以推广。

确保除了阅读本节之外,还要阅读有关选择的构建工具的性​​能的信息,例如:

类型检查通常需要来自其他文件的信息,并且与诸如转换/发出代码等其他步骤相比可能会相对昂贵。由于类型检查可能会花费更长的时间,因此会影响内部开发循环-换句话说,您可能经历更长的编辑/编译/运行周期,这可能令人沮丧。

因此,某些构建工具可以在单独的进程中运行类型检查而不会阻止发射。虽然这意味着可以在TypeScript报告构建工具中的错误之前运行无效代码,但通常您会经常首先在编辑器中看到错误,并且只要运行代码,您就不会受到任何阻碍。

一个实际的例子是Webpack的fork-ts-checker-webpack-plugin插件,或awesome-typescript-loader,有时也可以这样做。

默认情况下,TypeScript的发射需要语义信息,这些信息可能不是文件本地的,这是为了了解如何发射诸如const枚举和命名空间之类的功能,但是需要检查其他文件以生成任意文件的输出会导致发射速度变慢。

对需要非本地信息的功能的需求很少见-可以使用常规枚举代替const枚举,并且可以使用模块代替命名空间。因此,TypeScript提供了isolatedModules标志来使由non-powered驱动的功能出错-local信息。启用isolatedModules意味着您的代码库对于使用TypeScript API的工具(例如transpileModule)或其他编译器(例如Babel)是安全的。

例如,下面的代码无法在运行时与隔离的文件转换一起正常工作,因为期望内联const枚举值。但幸运的是,isolatedModules会在早期告诉我们。

// ../src/fileA.ts export声明常量枚举E {A = 0,B = 1,} // ./src/fileB.ts import {E} from“ ./fileA”;安慰 。日志(E。A); //〜//错误:提供'--isolatedModules'标志时,无法访问环境const枚举。

请记住:isolatedModules不会自动使代码生成更快,它只是告诉您何时要使用可能不被支持的功能。您要寻找的是隔离的模块在不同的构建工具和API中发出。

babel-loader以一种隔离的方式编译文件(但自身不提供类型检查)。

插件会影响编辑器的使用体验。尝试禁用插件(尤其是与JavaScript / TypeScript相关的插件),看看是否可以解决性能和响应能力方面的任何问题。

某些编辑器也有自己的性能疑难解答指南,因此请仔细阅读它们。例如,Visual Studio Code也有自己的“性能问题”页面。

您可以使用带有--extendedDiagnostics的TypeScript来获得编译器花费时间的打印输出。

文件:6行:24906节点:112200标识符:41097符号:27972类型:8298使用的内存:77984KA可分配性缓存大小:33123身份缓存大小:2子类型缓存大小:0I / O读取时间:0.01s解析时间:0.44s编程时间:0.45s绑定时间:0.21s检查时间:1.07 stransformTime时间:0.01scomment时间时间:0.00s I / O写入时间:0.00sprint时间时间:0.01s发射时间:0.01s总时间:1.75s

请注意,“总时间”将不是它之前所有时间的总和,因为存在一些重叠且未检测到某些工作。

程序包含的文件数(使用--listFiles查看它们是什么)。

从文件系统读取,扫描和解析程序以及对程序图进行其他计算所花费的总时间。这些步骤在此处混合并合并在一起,因为一旦通过导入和导出将它们包含进来,就需要解析和加载文件。

将TypeScript AST(代表源文件的树)重写为在较旧的运行时中可以使用的形式所花费的时间。

计算输出文件的字符串表示形式并将其发送到磁盘所花费的时间。

文件数/代码行数是否大致与项目中的文件数相对应?如果没有,请尝试运行--listFiles。

编程时间或I / O读取时间看起来是否很高?确保正确配置了包含/排除设置。

其他时间似乎不对吗?您可能要提出问题!您可以做的有助于诊断的事情可能是

运行tsc时,运行编译所用的设置并不总是很明显,尤其是考虑到tsconfig.jsons可以扩展其他配置文件时。 showConfig可以解释tsc将为调用计算的内容。

使用traceResolution运行可以帮助解释为什么文件包含在编译中。emit有点冗长,因此您可能需要将输出重定向到文件。

如果找到不应该存在的文件,则可能需要研究修复tsconfig.json中的包含/排除列表,或者,可能需要调整其他设置,如类型,typeRoots或路径。

很多时候,用户会使用Gulp,Rollup,Webpack等第三方构建工具来降低性能,而与tsc --extendedDiagnostics一起运行以发现使用TypeScript和该工具之间的主要差异可能表明外部配置错误或效率低下。

tsc和具有TypeScript集成的构建工具之间的构建时间是否存在重大差异?

如果构建工具提供了诊断信息,TypeScript的分辨率与构建工具的分辨率之间是否有区别?

生成工具是否具有可能与其TypeScript集成有关的配置? (例如ts-loader的选项?)

有时TypeScript的类型检查可能会受到计算量大的.d.ts文件的影响,这种情况很少见,但可能会发生。可能已经还原了回归)通常可以解决问题。

故障排除后,您可能想探索一些常见问题的解决方法。如果以下解决方案不起作用,则值得提出问题。

如果您的项目已经正确配置并达到最佳配置,则可能要提出问题。

性能问题的最佳报告包含问题的容易获得和最小程度的重现。换句话说,可以轻松地通过仅包含几个文件的git克隆代码库,它们不需要与构建工具进行外部集成-它们可以是通过tsc调用或使用使用TypeScript API的隔离代码。需要对复杂的调用和设置进行编码的代码库无法确定优先级。

我们知道这并不总是容易实现的-特别是因为很难在代码库中隔离问题的根源,并且因为共享知识产权可能是一个问题。在某些情况下,团队将愿意发送保密协议(NDA),如果我们认为此问题影响重大。

无论是否可以复制,在提交问题时遵循以下说明将有助于我们为您提供性能修复。

有时,您会在构建时间和编辑场景中看到性能问题。在这种情况下,最好专注于TypeScript编译器。

首先,应该使用每晚版本的TypeScript来确保您没有遇到已解决的问题:

已安装的TypeScript版本(即npx tsc -v或yarn tsc -v)

重要的是,通过运行带有--trace-ic标志的Node.js v10 +和带有--generateCpuProfile标志的TypeScript,为团队提供诊断跟踪:

在这里./node_modules/typescript/lib/tsc.js可以替换为安装TypeScript编译器版本的任何路径,并且tsconfig.json可以是任何TypeScript配置文件。 profile.cpuprofile是您选择的输出文件。

--generateCpuProfile将发射到具有您选择的名称的文件。在上面的示例中,它将是一个名为profile.cpuprofile的文件。

⚠警告:这些文件可能包含工作空间中的信息,包括文件路径和源代码。这两个文件都可以以纯文本格式读取,并且可以在将它们作为GitHub问题的一部分附加之前对其进行修改。 (例如,为它们清除可能暴露仅供内部使用的信息的文件路径)。

但是,如果您有关于在GitHub上公开发布这些内容的任何疑问,请告诉我们,您可以私下共享详细信息。

感知的编辑性能通常受许多因素影响,并且TypeScript团队控制范围内的唯一事情是JavaScript / TypeScript语言服务的性能以及该语言服务与某些编辑器(例如Visual Studio,Visual Studio Studio代码,Visual Studio for Mac和Sublime Text)。确保在编辑器中关闭所有第3方插件,以确定TypeScript本身是否存在问题。

涉及性能问题的编辑稍微多一点,但是同样的想法也适用:可克隆的最小repro代码库是理想的,尽管在某些情况下,该团队将能够签署NDA来调查和隔离问题。

包括tsc --extendedDiag的输出

......