TypeScript: Object spread can lead to unknown properties added [Regression v3.1.6]
TypeScript Version: 3.6.0-dev.20190719
Search Terms: spread type check
Code
// Error as expected, b2 is not a valid key
const foo: {a?: string; b?: string} = {
//a: "",
...{b2: ""}
}
// I meant to overwrite b, but somehow this is accepted
const bar: {a?: string; b?: string} = {
a: "",
...{b2: ""}
}
Expected behavior:
bar should not accept ...{b2: ""}
Actual behavior:
bar has type {a?: string; b?: string}, but at runtime actually has extra property b2
Playground Link: No problem v3.0.1 Regression v3.1.6
Related Issues: #30129
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 11
- Comments: 15 (4 by maintainers)
...{}is really rare, so checking spreads of object literals differently from other spreads would complicate the compiler with no real benefit.I grepped for
...{in all the .js/.ts files in our user and, besides tests in prettier and flow, found one use, which looks like it was introduced by a mechanical upgrade fromObject.assign.I can’t believe it. It would avoid so many bugs…
We decided to track excess properties only for actual properties, not for spreads, back in 2017. See the design meeting notes on Spread object literal freshness at #14853.
Eh, this bug is “working as intended”? Well, I guess I question your “intentions” if you are allowing values to mismatch types in a statically typed language. But only sometimes, other times excess property check is working as users expect it to.
I don’t see how it is arguable. In my statically typed point of view it definitely should be an error, or at the very least a user should be able to enable a compiler option for it being an error.
We probably will have to use properly typed lenses (and forbid object spreads) to overcome this “working as intended” blunder, which already caused a bunch of bugs in our code which could have been caught early if the compiler would have done its job.
Believe me, I would love to, but I am stuck at work with TypeScript.
But what if we don’t use subtyping?
TypeScript here can statically verify it’s an error. Why both cases aren’t reported as errors or at least cannot be enabled in compiler to report them as errors? This looks to me like pretty bad inconsistency.
Typescript, along with any language with subtyping (including Hegel), allows this:
I don’t like subtyping, but it’s unavoidably part of Javascript, and nothing to do with whether Typescript is sound or not. If you want to avoid this problem, use a language without subtyping.
@sandersn I’d say spreading is quite common in JSX when a wrapper component relays props to its children.
this could be very useful in React.
regarding this matter Flow does better https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+IFkolXpUCWewUEAwhCQgRDH8wEH4hMnwROHlsNnw4KHwwSLAAC3wANyo4LFxscWQuHgMNZmwsiRSAWglaY1cq-hIAa2wJXNpG4Vxcdvdu3v7B0RxKUYMhKDBSqmbWwIq3eagoOrKSKgH0wtMMPznY7d2SfcoBiEZ-aG5G3Ix085AF-ZhsRoRehqUEiNMgSQHlSruBZxJrMcJwMhzAC+-EgGiCLWMAAIALK0AAqayxAF4scAADpQLFYjDILHCEQzADclKRlMptnsWKO3DgbBJZMp1NpWIAjLwhVi0HSAEysllQSkAeiV9IgUWwWIkOUiWIABjzcHy9dySGqJDTrogoCVNWF9XjCcY9RzoFzpbiCUTScAaXTxVLZUiFcrVWhQliAO6aqgxtClLUQLWmMBdbkWyNwbVYpiefn3EyRCCUV12C0ix3eslYqy1w18rFImIgfImEhwaBBfIABisMtFAHYrN2QEigA
Would love too ! This code should definitely not be correct:
const v: { a: string } = { a: '', ...{ b: '' } };Would love to see this fixed, since it can lead to very undesirable outcomes (inadvertently polluting strictly typed store state, values of forms, data for saving to db, request bodies).
The meeting notes from #14853 were written 3 years ago. Isn’t type inference better nowadays, which would allow TypeScript to really know which properties the spread object has?