Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

erroneous "is specified more than once, so this usage will be overwritten.(2783)" #39671

Closed
clausreinke opened this issue Jul 20, 2020 · 5 comments
Labels
Duplicate An existing issue was already created

Comments

@clausreinke
Copy link

TypeScript Version: 3.9.2, also in 4.0.0-beta (not in 3.8.3)

Search Terms:

2783

Expected behavior:

code should be accepted without error

Actual behavior:

error 2783 ist raised

Related Issues:
#38535, #39113, #36779, #37740

Code
(the actual code builds obj in a loop, but this small example shows the issue; btw, if you delete the x: 0,, there is no error, which is also not correct)

let obj: {[key: string]: {x: number}} = {};
// erroneous type error:
//      'x' is specified more than once, so this usage will be overwritten.(2783)
//      input.tsx(3, 21): This spread always overwrites this property
obj['key'] = {x: 0, ...obj['key']}; // x would be in obj['key'], if 'key' was in obj
Output
"use strict";
let obj = {};
// erroneous type error:
//      'x' is specified more than once, so this usage will be overwritten.(2783)
//      input.tsx(3, 21): This spread always overwrites this property
obj['key'] = Object.assign({ x: 0 }, obj['key']); // x would be in obj['key'], if 'key' was in obj
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "moduleResolution": 2,
    "target": "ES2017",
    "jsx": "React",
    "module": "ESNext"
  }
}

Playground Link: Provided

potentially relevant: https://stackoverflow.com/questions/47155141/spreading-undefined-in-array-vs-object

@MartinJohns
Copy link
Contributor

Duplicate of #37688, #39113, #38589, and probably more. Used search terms: overwritten

@clausreinke
Copy link
Author

yes, there are multiple related issues, including some apparently fixed ones; problem is, they usually have an additional quirk, which is often the part that gets fixed;

I tried to reduce our issue to a small example with nothing extra, so it is not a duplicate of any of the listed issues. There may well be a common root to several of the related issues, and there are sufficiently many related issues that a collective tracking issue may be in order.

@RyanCavanaugh
Copy link
Member

The real duplicate here is #13778 since the fix is to include undefined in the type of obj['key']

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jul 20, 2020
@clausreinke
Copy link
Author

while that is a more interesting discussion (I'm only halfway through at this point) that clarifies some of the goals that led to the current situation, neither the fix nor the problem are quite as simple.

In the real-world code, we reconstruct a tree from a flat database representation, and we know that the order in which the code accesses the tree while the tree is being build is correct. We just find it difficult to convince typescript's CFA of this fact.

I've extended the playground example a bit in the hope of illustrating the effect: Playground Link

  • if we do not declare the map as Partial, typescript complains about overwriting
  • if we do declare the map as Partial, typescript complains about missing fields

Is our only option to disable typechecking here?

@clausreinke
Copy link
Author

okay, while thinking some more about #13778 and about how the example still doesn't quite represent our problem, I was able to come up with a way to explain to typescript what is happening. For others finding this issue:

  • we were building up our map, so it starts out empty (Partial) and ends up full (not Partial) and we happen to know for each key, when it is either (otherwise we could not be sure our code works)
  • so we were able to take the basic map type and coerce it as neccessary:
    • Partial<MapType> to begin with
    • obj[key] as MapType[keytype] when we are certain the key has been filled

slightly extended example: Playground Link

Note

  • in line 10, we have a default value for x potentially overwritten by the spread of a Partial
  • in line 11, we have a spread of a non-Partial, overwriting y

I'm still on the fence whether the change from 3.8.3 to 3.9.2 was positive or negative in general. For this example:

  • [pro] the additional type annotations express some of the reasoning behind why the code works as it does
  • [contra] typescript started marking perfectly valid code as incorrect and we had to go in and convince the compiler that the code was indeed correct as it stands

bumi added a commit to bumi/joule-extension that referenced this issue Jul 29, 2020
The use of the spread opertor causes issues in TypeScript 3.9.x
To avoid this issue we use Object.assign to return on object with
default values.

TypeScript error:
error TS2783: '<XYZ>' is specified more than once, so this usage will be overwritten.

See Travis build with errors:
https://travis-ci.org/github/joule-labs/joule-extension/builds/713053315

Related TypeScript issues:

microsoft/TypeScript#39671
microsoft/TypeScript#38535
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants