TypeScript [构建性能]包括yup增加了3秒的lib类型检查

fcy6dtqo  于 4个月前  发布在  TypeScript
关注(0)|答案(6)|浏览(65)

Bug报告

在我们(私有)使用组合项目的TypeScript项目中,随着更多项目的添加,我们一直在努力解决编译时间的增加问题。我根据 Performance Tracing Docs 进行了调查。最终我发现,在每个包构建过程中消耗 yup 会导致大约3秒的延迟(在一台M1 MBP上)。鉴于仓库中有100多个组合项目(并非所有项目都受到影响,因为并非所有导入 yup 的项目都受到影响),这会成为一个非平凡的构建时间增加。
可以通过将 skipLibCheck 设置为 true 来缓解这个问题,但理想情况下,这对于下游类型的完全验证应该是不正确的。

🔎 搜索词

性能 yup lib 类型检查

🕗 版本与回归信息

  • 在每个版本中我都尝试了这种行为,我也查阅了常见问题解答,但没有找到适用的内容

⏯ Playground链接

带有相关代码的Playground链接
考虑到这是一个性能bug,上述Playground链接并不是特别有用。我还准备了一个最小复现案例。https://github.com/berickson1/Playground/tree/master/type-checking-perf

💻 代码

import yup from 'yup'

export function testFunction(): void {
    const yupString = yup.string()
    console.log(yupString);
}

🙁 实际行为

在一台M1 MBP上,单个项目的编译时间从几秒钟减少到超过3秒。在使用组合项目时,对于包含 yup 的每个包,这种影响更大。

跟踪ZIP:
trace.json.zip

🙂 预期行为

当包含 yup 时,项目编译时间会有一个微不足道的增加。这可以通过以下两种方式之一来管理:1)提高类型检查的性能;2)利用共享的内存缓存,使node_module lib类型在组合构建之间共享。

u2nhd7ah

u2nhd7ah1#

我不太确定如何将这个转化为可操作的内容。类型只是非常复杂。以下是 date.d.ts 的代码片段:

export interface RequiredDateSchema<TType extends Maybe, TContext extends AnyObject = AnyObject> extends DateSchema<TType, TContext, NonNullable> {
// Cheap half
defined(msg?: MixedLocale['defined']): DefinedDateSchema<TType, TContext>;
required(msg?: MixedLocale['required']): RequiredDateSchema<TType, TContext>;
optional(): DateSchema<TType, TContext>;
notRequired(): DateSchema<TType, TContext>;

// Expensive half
default<D extends Maybe<TType>>(def: Thunk<D>): If<D, RequiredDateSchema<TType | undefined, TContext>, RequiredDateSchema<Defined<TType>, TContext>>;
nullable(isNullable?: true): RequiredDateSchema<TType | null, TContext>;
nullable(isNullable: false): RequiredDateSchema<Exclude<TType, null>, TContext>;

}

注解掉 "expensive half" 可以节省大约 0.4 秒(!!),但很容易看出原因 -- 验证所有这些示例是否合法是一项巨大的工作。我们可能可以让它稍微快一点,但我们无法检查得足够快,以至于它不会对整体构建性能产生可测量的影响。即使从 3 秒提高到 0.3 秒,仍然会在多个构建中累积很多。

我们不可能在构建之间重用验证结果,因为组合构建中的每个项目可能具有不同的设置或引入不同的类型(实际上,在大多数构建中,后者几乎肯定是这种情况),这两者都可能影响任意库类型检查是否成功。

如果你正在拉取其他人发布到 npm 的 lib 文件,那么 skipLibCheck 只是一件非常非常安全的事情,我们普遍推荐它就是因为这个原因。

pobjuy32

pobjuy322#

我亲眼看到过一些下游类型的类型断开,因为软件包升级而发生,所以为了安全起见,我们将其禁用。
我们的仓库中还有一些复杂的递归类型 - 因此在这里的改进可能会对其他人产生下游效益,但很难直接列举。我正在对我们的项目进行一些性能分析,以识别和改进热点。
话虽如此,我会继续设置 "skipLibCheck": true 来实现我们在构建中的一些收益。

t40tm48m

t40tm48m3#

@amcasey 和我讨论后的想法:

  • 添加一个构建模式开关,使得每次构建操作只检查 .d.ts 文件一次,即使这可能并不正确(命名为 --skipLibCheckYolo 或其他名称)
  • 以某种“环境”定义为基础,将项目分组(设置、相关的全局增强等),并假设它们的大小不会太大,一次性通过检查器传递多个项目。我们可以乐观地根据先前构建的 tsbuildinfo 进行分组,然后在某个假设无效时退出。有很多具有“许多太小的项目”设置的项目会从这个想法中受益匪浅,但以一种能够很好地摊销的方式实现起来非常困难,因为有很多信息需要计算“环境”。
  • 我在输入这些内容时想到的一个子想法:我们可以让项目明确定义一个“队列”属性,表示“我保证与其他同一队列中的项目一起分组是安全的”。这将正确性的压力放在用户身上(当然是一种权衡),但可能可以在单独的遍历过程中静态验证( tsc --build --check-cohorts?)。
a1o7rhls

a1o7rhls4#

翻译结果为:我认为这两个选项中的任何一个都有助于我们的场景-我们的设置基本上是模板化的tsconfig文件(见下文),所以不应该包含任何其他类型。在上述选项中,我确实喜欢分组的概念-这样可以非常明确地表明行为是相互关联的(也许可以放置一个简单的检查器来验证同一组中的tsconfig.json文件是否一致)

t8e9dugd

t8e9dugd5#

顺便说一下,在4.6版本中,我们因为#46599而在yup上表现得更好。对于yup的已输入包的检查时间从4.5的5.0秒减少到4.6的1.5秒,减少了3倍多。

i2loujxw

i2loujxw6#

顺便说一下,在4.6版本中,我们对yup的处理效果要好很多,这得益于#46599。对于yup的检查时间,使用4.5版本时是5.0秒,而使用4.6版本时只需1.5秒。这比之前的3倍减少还要多。
我可以确认,设置skipLibCheck: false可以将我测量到的yup处理时间(约3秒)降低到大约600毫秒,这主要得益于使用了3个高亮显示的昂贵的.d.ts文件和"typescript": "4.6.0-dev.20211120",

相关问题