microsoft / microsoft/TypeScript

Regression: recursive conditional+mapped "serialize" type no longer proves `Transform<X>` assignable to `X` on deeply-nested compound types (native-preview; works in tsc)

オープン
#63,845 コメント 9 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Needs More Info
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

Environment

  • @typescript/native-preview (tsgo)
  • Last good: 7.0.0-dev.20260421.2
  • Broken: 7.0.0-dev.20260618.17.0.0-dev.20260622.1 (latest) — all fail
  • Stable typescript@5.9.3: OK (accepts the code)

So: same source, only the native-preview version changed → a tsgo regression somewhere between 20260421.2 and 20260618.1.

What breaks

A common recursive "serialize" utility type — maps Date | bigintstring recursively through objects and arrays, leaving everything else unchanged (so for a type X containing no Date/bigint, Transform<X> is structurally identical to X):

type Transform<Base, From, To> = {
  [K in keyof Base]: Exclude<Base[K], undefined | null> extends never ? Base[K]
    : Exclude<Base[K], undefined | null> extends object ? (Exclude<Base[K], undefined|null> extends From ? To | Extract<Base[K], null|undefined> : Transform<Base[K], From, To>)
    : Base[K];
};
type TransformJson<T> = Transform<T, Date | bigint, string>;

For a deeply-nested compound type — recursion + arrays + an 8-member discriminated union with a shared base ({base} & ({type:1;…} | {type:2;…} | …)) nested several levels behind arrays — tsgo fails to prove TransformJson<X> assignable to X even when X has no Date/bigint (i.e. when the transform is provably the identity):

error TS2322: Type 'Transform<…, bigint | Date, string>' is not assignable to type '…'.
  Type 'Transform<{base} & (…union…), bigint | Date, string>' is not assignable to '{base} & (…union…)'.

tsc proves this assignable; tsgo (new builds) does not.

Diagnosis / things tried (in case it helps narrow the regressed area)

  • Adding an up-front "does this subtree contain From?" guard so the transform returns Base verbatim for no-From subtrees → flips the failure into TS2589 "Type instantiation is excessively deep and possibly infinite" across the project, i.e. the extra recursive pass exhausts a depth/instantiation budget.
  • Bounding the transform's recursion depth (no extra pass) → tsgo then fully expands the type (no TS2589) but still rejects the expanded {base} & union intersection-with-union assignment.
  • So it looks like two compounding limits: (a) a recursion/instantiation budget that's tighter or counted differently than in tsc, and (b) assignability of a mapped-type result back to an intersection-with-union, at depth.

Reproduction note (the awkward part)

I could not reduce this to a small standalone repro: the minimal/standalone versions of the exact pattern (even with the same 8-member intersection-with-union behind arrays) compile clean under both tsgo and tsc. It only reproduces inside a large real project (many hundreds of interacting Transform<…> instantiations across the program), which points at a scale-dependent / cumulative instantiation budget rather than a single-type defect.

Happy to help bisect against the dev-build range, or to share a private reproduction with the team if that's an option.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、7.0.0-dev.20260421.2 と 7.0.0-dev.20260618.1 の間で native-preview ビルドを bisect し、報告されたプロジェクトパターンで tsgo と tsc を比較します。再帰またはインスタンス化の予算と、深い mapped-type の代入可能性を調査します。完了の条件は、リグレッションを再現でき、TS2589 を回避し、既存の動作を壊さずに代入を復元できることです。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
go, typescript
領域
compilers
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
静か
明瞭さ
説明が足りない
初心者へのやさしさ
28/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。