Skip to content

Commit

Permalink
[flow] Make optional ref prop in component syntax work
Browse files Browse the repository at this point in the history
Summary:
After the libdef change to make React.RefSetter include null and void, component syntax with optional ref prop already **almost** work. The only missing piece is the ref extraction.

If we don't have this logic, `void ~> ExtractRefT` will happen first, which will taint the solution with a mixed, which explains the reason of the weird error [here](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkO4AX34kA0STMjAglAkAAIAEqsZRcmCUdJcgDkUWUooA3AAdKByyAidlfHxcgBixAAFCwYAB+ZC8-kxPkwADKuJMAB5gDBiAb7KYoAheFzMJQDcIRDd6QA+ACUXOAcq5IcN0SslIAkkqTJk4A1sAAJYFsNrazQuzUBgC8PsDwdDhZYEiElCggdtEANotFLrdBoAzPSCyHGVyANoAXT9La5xdLUFlUGb8qgGieVGwXNs9ld7BNBr54ZN5uJVptdq5Doqzrn7tdxGew59Q-HuEn0+gs4QxDYC7DyisK4tlGtlftUh3daoHuuJl9Q6WhqEB9po2bAJgd6aPSXIAPQnnKQHEKBMDgTeEBQTAMHwYBwEoeBDQQHAbBcgADNhCFQEhIE4XKuQgAm+JwNASQNKRVgAEwACwAGxWKRID0kAA).

We can just special case this and not flow void, since it's not helpful to pin down the solution anyways.

Changelog: [internal]

Reviewed By: jbrown215

Differential Revision: D62779302

fbshipit-source-id: 86eb4c95ae5a13d4dff1b051eb879cd2c24e6d1d
  • Loading branch information
SamChou19815 authored and facebook-github-bot committed Sep 19, 2024
1 parent 927449d commit c0c9e46
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,8 @@ struct
rec_flow cx trace (t, u)
| (OptionalT _, ResolveUnionT { reason; resolved; unresolved; upper; id }) ->
resolve_union cx trace reason id resolved unresolved l upper
| (OptionalT { reason = _; type_ = t; use_desc = _ }, ExtractReactRefT _) ->
rec_flow cx trace (t, u)
| (OptionalT { reason = r; type_ = t; use_desc }, _) ->
let void = VoidT.why_with_use_desc ~use_desc r in
rec_flow cx trace (void, u);
Expand Down
53 changes: 52 additions & 1 deletion tests/component_syntax/component_syntax.exp
Original file line number Diff line number Diff line change
Expand Up @@ -2885,6 +2885,57 @@ Cannot use `T` in a `ref` property because it is a type parameter. The `ref` pro
^^^


Error ---------------------------------------------------------------------------------------------------- refs.js:96:13

Cannot create `Foo` element because in property `ref`: [incompatible-type]
- Either number [1] is incompatible with boolean [2] in property `bar` of the first parameter.
- Or property `current` is missing in function type [3] but exists in object type [4].

refs.js:96:13
96| <Foo ref={badRef} />; // error
^^^^^^

References:
refs.js:92:66
92| declare component Foo(ref?: React.RefSetter<{foo: string, bar: number}>)
^^^^^^ [1]
refs.js:94:60
94| declare const badRef: React.RefSetter<{foo: string, bar: boolean}>;
^^^^^^^ [2]
refs.js:94:25
94| declare const badRef: React.RefSetter<{foo: string, bar: boolean}>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
<BUILTINS>/react.js:181:5
181| | { -current: React$ElementRef<ElementType> | null, ... }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [4]


Error ---------------------------------------------------------------------------------------------------- refs.js:96:13

Cannot create `Foo` element because in property `ref`: [incompatible-type]
- Either number [1] is incompatible with boolean [2] in property `bar` of property `current`.
- Or a call signature declaring the expected parameter / return type is missing in object type [3] but exists in
function type [4].

refs.js:96:13
96| <Foo ref={badRef} />; // error
^^^^^^

References:
refs.js:92:66
92| declare component Foo(ref?: React.RefSetter<{foo: string, bar: number}>)
^^^^^^ [1]
refs.js:94:60
94| declare const badRef: React.RefSetter<{foo: string, bar: boolean}>;
^^^^^^^ [2]
refs.js:94:25
94| declare const badRef: React.RefSetter<{foo: string, bar: boolean}>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
<BUILTINS>/react.js:182:6
182| | ((React$ElementRef<ElementType> | null) => mixed)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [4]


Error ---------------------------------------------------------------------------------------- refs_without_react.js:1:1

Cannot declare component because component Foo [1] is not guaranteed to reach a return statement. An explicit return
Expand Down Expand Up @@ -3351,7 +3402,7 @@ Strict mode function may not have duplicate parameter names



Found 222 errors
Found 224 errors

Only showing the most relevant union/intersection branches.
To see all branches, re-run Flow with --show-all-branches
12 changes: 12 additions & 0 deletions tests/component_syntax/refs.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,15 @@ import * as React from 'react';
component Bad<T>(ref: React$RefSetter<T>) { return null };

component Bad2<T>(ref: React$RefSetter<Array<Array<Array<T>>>>) { return null };

{
declare component Foo(ref?: React.RefSetter<{foo: string, bar: number}>)

declare const badRef: React.RefSetter<{foo: string, bar: boolean}>;
declare const goodRef: React.RefSetter<{foo: string, bar: number}>;
<Foo ref={badRef} />; // error
<Foo ref={goodRef} />; // ok
<Foo ref={null} />; // ok
<Foo ref={undefined} />; // ok
<Foo />; // ok
}

0 comments on commit c0c9e46

Please sign in to comment.