Skip to content

Fix type inference ordering inconsistency with discriminated unions and flatMap#3301

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-discriminated-union-flatmap
Draft

Fix type inference ordering inconsistency with discriminated unions and flatMap#3301
Copilot wants to merge 2 commits intomainfrom
copilot/fix-discriminated-union-flatmap

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 30, 2026

When inferring a common supertype from candidates, Go and TypeScript can encounter candidates in different orders due to union constituent ordering differences. This caused flatMap callbacks returning discriminated union branches to fail in Go but succeed in TypeScript.

Root Cause

getSingleCommonSupertype used isTypeSubtypeOf as the fallback tiebreaker. The subtype relation sets requireOptionalProperties = true for non-fresh object types, so a widened { op: "remove" } (which lost ObjectFlagsObjectLiteral after getWidenedTypeOfObjectLiteral) would fail the subtype check against InputOp = { op: "add" } | { op: "remove"; value?: Array<unknown> } — despite being clearly assignable to it. With reversed candidate ordering, Go picked the wrong type.

Fix

Replace the isTypeSubtypeOf fallback with asymmetric assignability (isTypeAssignableTo(s, t) && !isTypeAssignableTo(t, s)):

  • One-way assignable pairs (e.g. { op: "remove" }InputOp) correctly promote to the more general type regardless of ordering.
  • Mutually-assignable pairs (e.g. { x: number; y?: number } and { x: number; z?: number }) are symmetric, so selection remains order-based — preserving existing behavior.
export type InputOp = { op: "add" } | { op: "remove"; value?: Array<unknown> };
export type OutputOp = { op: "add" | "remove" };

export function f(operations: InputOp[]): OutputOp[] {
  return operations.flatMap((operation) => {
    if (operation.op === "remove" && operation.value) {
      return [].map(() => ({ op: "remove" }));
    } else {
      return [operation];
    }
  });
}

This follows the approach suggested by @ahejlsberg in the issue.

…ty as tiebreaker in getSingleCommonSupertype

Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/47e26ee2-385e-4444-a9ef-5e5b6ba3ed27

Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix inconsistency involving discriminated union and flatMap Fix type inference ordering inconsistency with discriminated unions and flatMap Mar 30, 2026
Copilot AI requested a review from RyanCavanaugh March 30, 2026 21:33
@jakebailey
Copy link
Copy Markdown
Member

This caused one break: microsoft/TypeScript#63328 (comment)

@typescript-bot
Copy link
Copy Markdown

@RyanCavanaugh Here are the results of running the user tests with tsc comparing main and refs/pull/3301/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Git clone failed"

Otherwise...

Everything looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistency involving discriminated union and flatMap

4 participants