Skip to content

Commit

Permalink
[spreads] Allow primitive spreads
Browse files Browse the repository at this point in the history
Summary:
Spreading numbers, strings, and bools all result in `{||}`. We can model that instead of saying that those aren't objects, which helps out RN with some of the patterns they like to use.

For example:
```
const styles = {...(showBlue() && {backgroundColor: 'blue'}), color: 'red'};
```
should pass type checking and is a very interesting + reasonable pattern!

object_kit handles a number of other object operations that are not spread, but I haven't verified that we have the correct behavior when we use this new rule in those operations. Until I verify those codepaths, I'm only turning this on for spreads.

Reviewed By: gabelevi

Differential Revision: D18270818

fbshipit-source-id: a4d1f4595cc9adaf8a81393f254d46457e3df1b3
  • Loading branch information
jbrown215 authored and facebook-github-bot committed Nov 1, 2019
1 parent 59f8d23 commit 5c09b8a
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 6 deletions.
14 changes: 14 additions & 0 deletions src/typing/object_kit.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,20 @@ module Kit (Flow : Flow_common.S) : OBJECT = struct
let flags = { frozen = true; sealed = Sealed; exact = true } in
let x = Nel.one { Object.reason; props = SMap.empty; dict = None; flags } in
resolved cx trace use_op reason resolve_tool tool tout x
(* TODO(jmbrown): Investigate if these cases can be used for ReactConfig/ObjecRep/Rest.
* In principle, we should be able to use it for Rest, but right now
* `const {x, ...y} = 3;` tries to get `x` from Number.
* They don't make sense with $ReadOnly's semantics, since $ReadOnly doesn't model
* copying/spreading an object. *)
| DefT (_, _, (StrT _ | NumT _ | BoolT _))
when match tool with
| ObjectWiden _
| Spread _ ->
true
| _ -> false ->
let flags = { frozen = true; sealed = Sealed; exact = true } in
let x = Nel.one { Object.reason; props = SMap.empty; dict = None; flags } in
resolved cx trace use_op reason resolve_tool tool tout x
(* mixed is treated as {[string]: mixed} except in type spread, where it's treated as
* {}. Any JavaScript value may be treated as an object and so this is safe.
*
Expand Down
14 changes: 10 additions & 4 deletions tests/new_spread/new_spread.exp
Original file line number Diff line number Diff line change
Expand Up @@ -964,12 +964,18 @@ References:
^^^^^^ [2]


Error ---------------------------------------------------------------------------------------------- type_string.js:1:15
Error ----------------------------------------------------------------------------------------------- type_string.js:2:2

string literal `foo` [1] is not an object.
Cannot cast `"foo"` to `O1` because string [1] is incompatible with `O1` [2].

1| type O1 = {..."foo"}; // error
^^^^^ [1]
type_string.js:2:2
2| ("foo": O1); // Error
^^^^^ [1]

References:
type_string.js:2:9
2| ("foo": O1); // Error
^^ [2]


Error ------------------------------------------------------------------------------------------------ type_union.js:9:2
Expand Down
4 changes: 2 additions & 2 deletions tests/new_spread/type_string.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
type O1 = {..."foo"}; // error
("foo": O1);
type O1 = {..."foo"}; // No error
("foo": O1); // Error
Empty file.
1 change: 1 addition & 0 deletions tests/objkit_primitives/objkit_primitives.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Found 0 errors
14 changes: 14 additions & 0 deletions tests/objkit_primitives/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@flow
const a: {||} = {...3};
const b: {||} = {...(3: 3)};
const c: {||} = {...''};
const d: {||} = {...('': '')};
const e: {||} = {...false};
const f: {||} = {...(false: false)};
const g: {||} = {...null};
const h: {||} = {...(null: null)};
const i: {||} = {...undefined};
const j: {||} = {...(undefined: void)};
declare function showBlue(): boolean;
const styles = {...(showBlue() && {backgroundColor: 'blue'}), color: 'red'};

0 comments on commit 5c09b8a

Please sign in to comment.