7.1 regression: comparing a recursive `-?` mapped type is quadratic in the number of properties and hits TS2589 (since typescript-go#4820)
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 48/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- go, typescript
- 领域
- compilers, performance
调研方向
Start by reading the variance computation change in microsoft/typescript-go#4820, then reproduce the issue with the supplied one-file repro and tsc -p . --singleThreaded --extendedDiagnostics. Compare instantiation counts for the last good and first bad builds, focusing on the recursive Builder interface, its -? mapped type, and changing S | K argument. Done means the comparison stays near linear in property count and the 500-property repro succeeds without TS2589.
由索引模型根据 Issue 内容生成。
描述
🔎 Search Terms
variance, TS2589, excessively deep, instantiation count, mapped type, -?, quadratic, 7.1 regression
🕗 Version & Regression Information
- Last good:
7.1.0-dev.20260805.1(gitHead 12318e599d) - First bad:
7.1.0-dev.20260806.1(gitHead 86cc4767d4) - That range is one commit: microsoft/typescript-go#4820, "Order variance computation by associated type symbol".
- Still failing on
7.1.0-betaand7.1.0-dev.20261024.1. - 7.0.2 accepts the code.
Same commit as #64891 and #64930, but this one is about performance: the comparison finishes with a correct result
for small types, it just costs quadratically more instantiations, and on real types it hits the 5M cap.
⏯ Playground Link
The 7.1 builds aren't available in the Playground. CLI repro: one file in an empty directory, tsc -p . --singleThreaded --extendedDiagnostics.
tsconfig.json
{ "compilerOptions": { "strict": true, "noEmit": true, "types": [] }, "files": ["repro.ts"] }
💻 Code
type D = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type Entity = Record<`f${'0' | '1' | '2' | '3' | '4'}${D}${D}`, string> // 500 properties
interface Method<T, K extends keyof T, S> {
(): Builder<T, S | K>
}
type Builder<T, S = never> = { [K in keyof T]-?: Method<T, K, S> }
declare const builder: Builder<Entity>
export const widened: Builder<Entity & object> = builder
🙁 Actual behavior
repro.ts(10,14): error TS2589: Type instantiation is excessively deep and possibly infinite.
Instantiations: 5003892 Check time: 79 s
🙂 Expected behavior
No error, as in 7.0.2 and 7.1.0-dev.20260805.1: about 9k instantiations, 0.04 s check time.
Additional information about the issue
With 100 properties (Record<`f${D}${D}`, string>) there is no error, but the count goes from 5,105 to 256,939
instantiations. It grows with the square of the property count: about 66k at 50 properties, 1M at 200 and the 5M cap
near 460.
| build | 100 props: instantiations | 500 props |
|---|---|---|
| 7.0.2 | 5,105 | 9,105, ok |
7.1.0-dev.20260805.1 (12318e599d) |
5,105 | 9,105, ok |
7.1.0-dev.20260806.1 (86cc4767d4) |
256,939 | TS2589 |
| 7.1.0-dev.20261024.1 | 258,143 | TS2589 |
All three of these are needed for the quadratic count; removing any one brings it back to linear:
- the
-?modifier on the homomorphic mapped type; Methodas a generic interface that referencesBuilderagain (with an inline() => Builder<T, S | K>both
7.0.2 and 7.1 are quadratic, so the regression is in how the interface's variance gets computed);- a type argument that actually changes, here
S | K(withBuilder<T, S>it stays linear).
The target also has to be a different but equivalent instantiation (Entity & object); a structurally identical
mapped copy of Entity does not trigger it.
Real-world impact: this is a reduced form of a fluent "selection builder" type from our library, whose
entity types resolve through UnionToIntersection<E> & object, so every selector is compared against
Builder<E & object>. On a ~2,100-file app under 7.1:
- TS2589 appears on ordinary selector call sites, and the callbacks' destructured parameters cascade into TS7031/TS7006.
- The depth error leaves the checker's caches in a bad state, so a later file that checks cleanly on its own reports
TS2339 (members missing on the builder's result type). The error count depends on--checkersand on which files
share a checker (0 to 77). - The project checks
--singleThreadedin 25 s / 2.6 GB on7.1.0-dev.20260805.1; on7.1.0-dev.20260930.4the
same run was OOM-killed at an 18 GB cap, and with default checkers it reached 24 GB before being killed.
- 主要语言
- Go
- 星标
- 112k
- 派生
- 15.4k
- 平均合并
- 3 天 1 小时
- 30 天内合并 PR
- 136
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
microsoft/TypeScript 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 62/100
microsoft/TypeScript#65290 ·
维护者通常 1 天内回复
-
Native API: formatDiagnosticsWithColorAndContext drops related information without a file location可能重新可做 关联的 PR 已关闭且未合并。 未关闭
难度 2/5 1-3 小时 新手友好度 75/100
microsoft/TypeScript#65087 ·
维护者通常 1 天内回复
-
TS7 LSP: documentHighlight returns nothing when the file path contains '@'可能重新可做 关联的 PR 已关闭且未合并。 未关闭Bug
难度 2/5 1-3 小时 新手友好度 78/100
microsoft/TypeScript#64758 ·
维护者通常 1 天内回复
-
Language server (tsc --lsp) exits with code 1 after `shutdown` and `exit`可能已有人在做 @rajnish-verma-22172 于 14 天前认领。 未关闭Bug
难度 2/5 1-3 小时 新手友好度 76/100
microsoft/TypeScript#64742 · 1 条评论 ·
维护者通常 1 天内回复
-
Non-enum CLI options with multiple values separated by comma and space aren't whitespace trimmed可能已有人在做 @RyanCavanaugh 于 28 天前认领。 未关闭Bug
难度 2/5 1-3 小时 新手友好度 68/100
microsoft/TypeScript#64618 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 microsoft/TypeScript 的全部 Issue
相似的 Issue
-
area:sampler tech-debt
难度 2/5 1-3 小时 新手友好度 72/100
jmbarzee/temporal-architect#302 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 66/100
riverqueue/river#1576 ·
维护者通常 1 天内回复
-
NFS StorageProvider renders a missing nfsSubDir as "<no value>"可能已有人在做 @ldming 今天认领。 未关闭kind/bug
难度 2/5 1-3 小时 新手友好度 82/100
apecloud/kubeblocks#11067 ·
维护者通常 1 天内回复
-
area:ui bug priority:low
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
RTX payload type is not found when the RTX fmtp line has rtx-time可能已有人在做 @RaphaelFakhri 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复