-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
$Required utility type #7571
$Required utility type #7571
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dsainati1 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
This looks like a good approach, but I have a couple of concerns. I'm not entirely convinced by how this utility type behaves with respect to class and instance types. You are able to cast something of type Your provided tests also have some discrepancies in them: you add an error comment on I'd also like to see a few tests that ensure that property variance is properly maintained by this utility. |
@dsainati1 it converts classes to objects, just like How can I seal object completely from flowing? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good overall, but I think you worked harder than you needed to.
If you move away from using a new use_t and instead implement $Required in object_kit (search for "and object_kit" in flow_js.ml) then you'll be able to reduce your changes quite a bit.
Polymorphism + type destructors is sometimes tricky, so please add a few tests to make sure $Required works in polymorphic functions.
src/typing/flow_js.ml
Outdated
@@ -7351,6 +7407,7 @@ and eval_destructor cx ~trace use_op reason t d tout = match t with | |||
| ReadOnlyType -> | |||
let open Object in | |||
ObjKitT (use_op, reason, Resolve (Next), ReadOnly, tout) | |||
| RequiredType -> RequiredT (reason, tout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding a new use type, can you follow the way ReadOnly is implemented in object_kit and do the same? It will significantly simplify your code by handling a ton of edge cases for you.
For example, your code will error on mixed/empty, which the object_kit code can handle more elegantly.
You also won't need the IncompatibleRequiredT because object kit handles errors with types that can't be converted into an object.
You also shouldn't need to distinguish between instances and objects, since object_kit will take care of that for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But object_kit doesn't have polarity, so it always becomes neutral
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also there is issue in object_kit with statics, maybe you know how to fix it?
#7572
I don't understand why contravariant properties get replaced with mixed in object_kit | None ->
let reason = replace_reason_const (RUnknownProperty (Some x)) r in
let t = DefT (reason, bogus_trust (), MixedT Mixed_everything) in
t
in |
@@ -1981,7 +1982,7 @@ and Object : sig | |||
(* A union type resolves to a resolved spread with more than one element *) | |||
and resolved = slice Nel.t | |||
|
|||
and slice = reason * props * dict * TypeTerm.flags | |||
and slice = reason * props * dict * TypeTerm.flags * Properties.t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added original props (raw_props) because I needed polarity from field structure, and also doesn't wanted to get mixed
on write-only properties. Not sure if this belongs here, or perhaps object_kit can be more generic
match p with | ||
| Field (_, DefT (_, _, OptionalT t), polarity) -> Field (None, t, polarity) | ||
| Field (_, t, polarity) -> Field (None, t, polarity) | ||
| Get (_, t) -> Get (None, t) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This handles getters/setters in object, but not in classes, not sure why
@@ -10985,15 +10988,16 @@ and object_kit = | |||
Obj_type.sealed_in_op reason flags1.sealed && | |||
Obj_type.sealed_in_op reason flags2.sealed; | |||
} in | |||
reason, props, dict, flags | |||
let raw_props = raw_props1 in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should merge original props also? But they don't get used here
Pick implemented with type RestUnsound<T1, T2> = $Rest<T1, $Required<T2>> // $Rest doesn't remove optional props
type Pick<T1, T2> = RestUnsound<
T1,
$ObjMap<RestUnsound<T1, T2>, <V>(V) => any>
>;
declare var picked: Pick<{| +a?: number, b?: string, c: string |}, {| a: any |}>;
picked.a; // void | number
picked.a = 1; // doesn't save polarity
picked.b; // error
picked.c; // error Avoids this bug #5936
|
@jbrown215 friendly ping here |
I can't make time for this yet. I'm spending all my time on a big hurdle in my spreads project. Once I get past that, this will be the first thing I review. |
The more i think of this, probably there should be more general solution, i.e. some mapper or even $ObjMap itself should be allowed to configure property type descriptor (similar to js, but not identical) type $PropertyDescriptor<T> = {|
optional: boolean,
enumerable: boolean, // ???
value: T,
polarity:
| '+' // positive
| '-' // negative
| 'N' // neutral
|}
type $ReadOnly<T> = $ObjModify<
T,
<Descriptor>(Descriptor) => {|...Descriptor, polarity: '+'|}
>
type $WriteOnly<T> = $ObjModify<
T,
<Descriptor>(Descriptor) => {|...Descriptor, polarity: '-'|}
>
type $ReadWrite<T> = $ObjModify<
T,
<Descriptor>(Descriptor) => {|...Descriptor, polarity: 'N'|}
>
type $Required<T> = $ObjModify<
T,
<Descriptor>(Descriptor) => {|...Descriptor, optional: false|}
> I realize that type destructors are tricky, and not all of them work well with each other, so perhaps generalization would only make it worse |
So after chatting to some people on the team I think we would like to decrease the number of |
@dsainati1 this can't be implemented with |
Fixes #5134
It doesn't touch
T | void
unions