Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 22, 2025

Fixes #61223

Problem

TypeScript incorrectly reported error TS2783 ("'x' is specified more than once, so this usage will be overwritten") when spreading union types in object literals, even when the property wasn't guaranteed to exist in all constituents of the union.

For example, this code incorrectly produced an error:

type Thing = {
    id: string;
    label: string;
};

function find(id: string): undefined | Thing {
    return things.find(thing => thing.id === id);
}

fun({
    id: 'foo',
    ...find('foo') ?? { label: 'Foo' },  // ❌ False positive: 'id' is specified more than once
});

The error claimed id would "always" be overwritten, but this was incorrect. The spread expression evaluates to Thing | { label: string }, and the fallback object { label: 'Foo' } doesn't have an id property at all, so id is only overwritten when find() returns a Thing.

Root Cause

The checkSpreadPropOverrides function only checked if a property had SymbolFlags.Optional before reporting the error. It didn't account for properties that exist in some constituents of a union type but not others. These properties are marked with CheckFlags.Partial (specifically ReadPartial or WritePartial), indicating they're only present in a subset of the union's constituents.

Solution

Added an additional check for CheckFlags.Partial in checkSpreadPropOverrides. Properties marked as partial in union types should not trigger the "always overwrites" error since they don't exist in all code paths.

// Only report error if property is both non-optional AND not partial
if (!(right.flags & SymbolFlags.Optional) && !(getCheckFlags(right) & CheckFlags.Partial)) {
    // Report error...
}

Test Coverage

Added comprehensive test file spreadUnionPropOverride.ts that verifies:

  • The original issue is fixed: spreading Thing | { label: string } no longer errors on id
  • Properties present in only some union constituents don't trigger false positives
  • Properties present in all union constituents still correctly trigger the error
  • Optional properties in unions are handled correctly
  • Required properties that exist in all union constituents still error as expected

The fix preserves all existing correct behavior while eliminating the false positive for partial properties in union types.

Original prompt

This section details on the original issue you should resolve

<issue_title>TS2783 False Positive</issue_title>
<issue_description>### 🔎 Search Terms

TS2783: "... is specified more than once, so this usage will be overwritten", "This spread always overwrites this property"

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about 5.9.3

⏯ Playground Link

https://www.typescriptlang.org/play/?esModuleInterop=false&target=11&jsx=0&verbatimModuleSyntax=true&strictBuiltinIteratorReturn=true&exactOptionalPropertyTypes=true&noImplicitOverride=true&noPropertyAccessFromIndexSignature=true&ts=5.8.3#code/C4TwDgpgBAKgFgSwHYHMoF4oG8BQV9QIAmAXFAM7ABOyKA3HgQDYCGARhE2ZTagwL4McAYwD2SSlGCJU5MvFoBtALoYoKoQDMArkmHAE4qJuREAFMW7VaASjK6iEE0ghEoAH1gy0uAlCoQwNpUSFLe5AB0zubStBgAfGG0EcQY6JjENgI4OI7CrAHGuvqGoTpIZrGo8t52UABuosRauma+BJZQAOSaoqJdADSM+BGj0WY9fV02UAD8s9jDflCsHFzdAGJTQ378Q-xZOEA

💻 Code

type Thing = {
    id: string;
    label: string;
};

const things: Thing[] = [];

function find(id: string): undefined | Thing {
    return things.find(thing => thing.id === id);
}

declare function fun(thing: Thing): void;

fun({
    id: 'foo',
    ...find('foo') ?? {
        label: 'Foo',
    },
});

🙁 Actual behavior

TS2783 on line 15: 'id' is specified more than once is a false positive.

🙂 Expected behavior

Expect no errors. The return type of find('foo') includes undefined, and the right-hand side of the nullish coalescing operation does not override the 'id' property.

Additional information about the issue

No response</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #62655


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Fix TS2783 false positive issue Fix TS2783 false positive for union types in object spread expressions Oct 22, 2025
Copilot AI requested a review from RyanCavanaugh October 22, 2025 06:05
Copilot finished work on behalf of RyanCavanaugh October 22, 2025 06:05
@RyanCavanaugh
Copy link
Member

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 22, 2025

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

Hey @RyanCavanaugh, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

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

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Git clone failed"

Otherwise...

Everything looks good!

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - node (v18.15.0, x64)
Errors 34 34 ~ ~ ~ p=1.000 n=6
Symbols 62,370 62,370 ~ ~ ~ p=1.000 n=6
Types 50,386 50,386 ~ ~ ~ p=1.000 n=6
Memory used 193,951k (± 0.96%) 193,445k (± 0.79%) ~ 192,723k 196,541k p=1.000 n=6
Parse Time 1.30s (± 0.31%) 1.30s (± 0.40%) ~ 1.29s 1.30s p=0.114 n=6
Bind Time 0.72s (± 0.71%) 0.72s (± 0.75%) ~ 0.72s 0.73s p=0.640 n=6
Check Time 9.75s (± 0.30%) 9.76s (± 0.16%) ~ 9.75s 9.79s p=0.359 n=6
Emit Time 2.75s (± 0.50%) 2.73s (± 1.05%) ~ 2.69s 2.77s p=0.511 n=6
Total Time 14.52s (± 0.23%) 14.52s (± 0.28%) ~ 14.48s 14.59s p=0.627 n=6
angular-1 - node (v18.15.0, x64)
Errors 2 2 ~ ~ ~ p=1.000 n=6
Symbols 955,117 955,117 ~ ~ ~ p=1.000 n=6
Types 415,619 415,619 ~ ~ ~ p=1.000 n=6
Memory used 1,254,627k (± 0.01%) 1,254,552k (± 0.00%) -75k (- 0.01%) 1,254,503k 1,254,595k p=0.045 n=6
Parse Time 6.50s (± 0.68%) 6.54s (± 0.86%) ~ 6.47s 6.61s p=0.197 n=6
Bind Time 1.88s (± 0.43%) 1.88s (± 0.22%) ~ 1.88s 1.89s p=0.584 n=6
Check Time 32.30s (± 0.34%) 32.18s (± 0.29%) ~ 32.07s 32.28s p=0.127 n=6
Emit Time 14.86s (± 0.42%) 14.86s (± 0.46%) ~ 14.78s 14.97s p=0.872 n=6
Total Time 55.54s (± 0.19%) 55.46s (± 0.23%) ~ 55.30s 55.60s p=0.229 n=6
mui-docs - node (v18.15.0, x64)
Errors 1 1 ~ ~ ~ p=1.000 n=6
Symbols 552,458 552,458 ~ ~ ~ p=1.000 n=6
Types 89 89 ~ ~ ~ p=1.000 n=6
Memory used 827,395k (± 0.00%) 827,391k (± 0.00%) ~ 827,375k 827,402k p=0.570 n=6
Parse Time 8.60s (± 0.22%) 8.60s (± 0.32%) ~ 8.57s 8.64s p=0.744 n=6
Bind Time 2.19s (± 0.29%) 2.19s (± 0.45%) ~ 2.18s 2.20s p=0.733 n=6
Check Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Emit Time 0.27s (± 3.77%) 0.27s (± 1.50%) ~ 0.27s 0.28s p=0.545 n=6
Total Time 11.06s (± 0.22%) 11.06s (± 0.24%) ~ 11.03s 11.10s p=0.871 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,237,247 1,237,247 ~ ~ ~ p=1.000 n=6
Types 259,866 259,866 ~ ~ ~ p=1.000 n=6
Memory used 2,364,769k (± 0.01%) 2,425,550k (± 6.10%) ~ 2,364,508k 2,727,750k p=0.128 n=6
Parse Time 5.14s (± 1.14%) 5.19s (± 1.24%) ~ 5.09s 5.28s p=0.228 n=6
Bind Time 1.76s (± 0.36%) 1.77s (± 0.46%) ~ 1.76s 1.78s p=0.177 n=6
Check Time 35.09s (± 0.35%) 35.12s (± 0.64%) ~ 34.68s 35.28s p=0.471 n=6
Emit Time 3.06s (± 2.71%) 3.03s (± 2.53%) ~ 2.96s 3.18s p=0.148 n=6
Total Time 45.08s (± 0.34%) 45.13s (± 0.52%) ~ 44.78s 45.44s p=0.575 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,237,247 1,237,247 ~ ~ ~ p=1.000 n=6
Types 259,866 259,866 ~ ~ ~ p=1.000 n=6
Memory used 3,159,182k (± 0.02%) 3,159,070k (± 0.02%) ~ 3,157,913k 3,159,993k p=1.000 n=6
Parse Time 6.86s (± 0.62%) 6.82s (± 1.03%) ~ 6.73s 6.89s p=0.471 n=6
Bind Time 2.15s (± 0.96%) 2.17s (± 1.50%) ~ 2.11s 2.20s p=0.126 n=6
Check Time 42.56s (± 0.39%) 42.66s (± 0.38%) ~ 42.46s 42.95s p=0.298 n=6
Emit Time 3.48s (± 1.74%) 3.52s (± 2.05%) ~ 3.40s 3.59s p=0.229 n=6
Total Time 55.04s (± 0.40%) 55.18s (± 0.32%) ~ 54.93s 55.42s p=0.297 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 262,536 262,536 ~ ~ ~ p=1.000 n=6
Types 104,041 104,041 ~ ~ ~ p=1.000 n=6
Memory used 440,682k (± 0.01%) 440,677k (± 0.01%) ~ 440,572k 440,733k p=0.936 n=6
Parse Time 3.53s (± 1.00%) 3.52s (± 0.88%) ~ 3.46s 3.54s p=0.624 n=6
Bind Time 1.32s (± 1.17%) 1.32s (± 0.62%) ~ 1.31s 1.33s p=1.000 n=6
Check Time 18.95s (± 0.11%) 18.97s (± 0.42%) ~ 18.86s 19.06s p=0.468 n=6
Emit Time 1.52s (± 0.90%) 1.53s (± 1.57%) ~ 1.50s 1.56s p=0.566 n=6
Total Time 25.32s (± 0.18%) 25.34s (± 0.36%) ~ 25.23s 25.45s p=0.520 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 72 72 ~ ~ ~ p=1.000 n=6
Symbols 225,367 225,367 ~ ~ ~ p=1.000 n=6
Types 94,290 94,290 ~ ~ ~ p=1.000 n=6
Memory used 369,984k (± 0.02%) 370,107k (± 0.04%) ~ 369,956k 370,317k p=0.128 n=6
Parse Time 2.82s (± 1.02%) 2.81s (± 0.70%) ~ 2.79s 2.85s p=0.371 n=6
Bind Time 1.60s (± 1.42%) 1.59s (± 0.92%) ~ 1.57s 1.61s p=0.684 n=6
Check Time 16.42s (± 0.27%) 16.45s (± 0.32%) ~ 16.40s 16.54s p=0.463 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 20.83s (± 0.21%) 20.86s (± 0.35%) ~ 20.80s 21.00s p=0.808 n=6
vscode - node (v18.15.0, x64)
Errors 5 5 ~ ~ ~ p=1.000 n=6
Symbols 3,961,269 3,961,269 ~ ~ ~ p=1.000 n=6
Types 1,243,017 1,243,017 ~ ~ ~ p=1.000 n=6
Memory used 3,751,287k (± 0.00%) 3,751,235k (± 0.01%) ~ 3,750,863k 3,751,483k p=0.936 n=6
Parse Time 15.29s (± 0.70%) 15.28s (± 0.76%) ~ 15.16s 15.44s p=0.810 n=6
Bind Time 4.98s (± 0.47%) 4.98s (± 0.61%) ~ 4.93s 5.02s p=1.000 n=6
Check Time 103.06s (± 3.48%) 104.01s (± 3.32%) ~ 100.62s 110.26s p=0.575 n=6
Emit Time 38.95s (±13.52%) 36.91s (± 7.96%) ~ 30.99s 38.52s p=0.689 n=6
Total Time 162.27s (± 2.65%) 161.19s (± 0.85%) ~ 159.45s 162.76s p=1.000 n=6
webpack - node (v18.15.0, x64)
Errors 40 40 ~ ~ ~ p=1.000 n=6
Symbols 373,614 373,614 ~ ~ ~ p=1.000 n=6
Types 163,495 163,495 ~ ~ ~ p=1.000 n=6
Memory used 530,050k (± 0.01%) 530,032k (± 0.02%) ~ 529,899k 530,184k p=0.688 n=6
Parse Time 4.52s (± 0.79%) 4.48s (± 0.60%) ~ 4.45s 4.51s p=0.105 n=6
Bind Time 1.96s (± 0.77%) 1.95s (± 1.86%) ~ 1.91s 2.00s p=0.332 n=6
Check Time 22.10s (± 0.37%) 22.11s (± 0.31%) ~ 22.00s 22.21s p=1.000 n=6
Emit Time 0.00s (±244.70%) 0.00s ~ ~ ~ p=0.405 n=6
Total Time 28.57s (± 0.29%) 28.54s (± 0.18%) ~ 28.47s 28.60s p=0.422 n=6
xstate-main - node (v18.15.0, x64)
Errors 30 30 ~ ~ ~ p=1.000 n=6
Symbols 676,516 676,516 ~ ~ ~ p=1.000 n=6
Types 203,680 203,680 ~ ~ ~ p=1.000 n=6
Memory used 578,449k (± 0.02%) 578,441k (± 0.02%) ~ 578,236k 578,570k p=0.936 n=6
Parse Time 4.19s (± 0.78%) 4.19s (± 0.65%) ~ 4.15s 4.22s p=0.935 n=6
Bind Time 1.34s (± 1.13%) 1.34s (± 0.41%) ~ 1.34s 1.35s p=0.227 n=6
Check Time 20.55s (± 1.95%) 20.52s (± 1.86%) ~ 20.21s 21.04s p=0.521 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 26.08s (± 1.49%) 26.05s (± 1.43%) ~ 25.77s 26.59s p=0.630 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

@RyanCavanaugh Here are the results of running the top 400 repos with tsc comparing main and refs/pull/62656/merge:

Everything looks good!

@jakebailey jakebailey marked this pull request as ready for review October 22, 2025 19:04
@Copilot Copilot AI review requested due to automatic review settings October 22, 2025 19:04
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@RyanCavanaugh RyanCavanaugh merged commit 542b095 into main Oct 22, 2025
33 checks passed
@RyanCavanaugh RyanCavanaugh deleted the copilot/fix-ts2783-false-positive branch October 22, 2025 23:44
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.

TS2783 False Positive Warn when tsconfig.json extends a file that contains references

4 participants