-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Constrained generic type parameters accept invalid updates #60623
Comments
Duplicate #10727 - type MySpecialImage = {
type: "image",
src: "neat",
} so it's not enough to validate that
|
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
While I agree that a type-level spread operator would be optimal, I don't think it's necessary here. Even if we model (T extends Image | Checkbox) & { src: number } ends up with
I agree it's not enough to validate that Fair enough on |
🔎 Search Terms
generic type variables unsafe modifications
,generic type variables mutation
,polymorphic function return type
,generic type variable invalid assignment
,polymorphic type variable unsound assignment
🕗 Version & Regression Information
⏯ Playground Link
delete
💻 Code
Given a variable
x
whose type is generic parameterT
, we can spread invalid updates alongsidex
, and the result is still assignable toT
:Likewise, we can delete required fields in
T
:🙁 Actual behavior
TypeScript does not error when clobbering or deleting a required field in a generically-typed value, and continues to infer the type of the original parameter, which is now invalid.
🙂 Expected behavior
I'd expect any of the following:
Simpler, most conservative: TypeScript prevents me from performing an update if it would invalidate any possible
T
.T
with an invalid type produces an error likeType 'number' is not assignable to type 'string'. (2322)
T
should produce the errorThe operand of a 'delete' operator must be optional. (2790)
Smarter, more complex: I can perform an update that is valid for some instantiations of
T
, but only after narrowing the type within the function body. Parametricity must also be preserved in the return type.src
field would only be permissible ifx
has been refined to typeT extends Checkbox
, wheresrc
can be treated as a superfluous property under the inexact object subtyping model. The narrowed branch must return a value assignable toT extends Checkbox
.userId
field would only be permissible ifx
has been refined to typeT extends { type: "guest" … }
, whereuserId
is optional. The narrowed branch must return a value assignable toT extends { type: "guest" … }
.Note that in both cases, we can narrow safely because
T
is constrained by a disjoint union of objects, where the standard depth and width subtyping rules apply. That is, whileT
could be instantiated with a different object likethis
T
must still contain all fields declared in the basemember
type, with types assignable to the corresponding fields.There is also a third option for immutable updates, as in the first example:
Allow updates, but refine the resulting type so it is not assignable to
T
: TypeScript could allow me to create the value{ ...x, src: 1 }
, but prevent me from returning this value as aT
.Currently, TypeScript considers
T & { src: number }
assignable toT
. But ifT extends Image | Checkbox
, then the resulting intersectioncannot satisfy the original constraint, and therefore cannot be returned by the function.
For what it's worth, Flow uses Option 3 for the case of spreading updates into a new object, and Option 1 for the case of mutating the original argument.
Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: