Skip to content

Commit

Permalink
[flow] Ban more kinds of usages of React dollar types
Browse files Browse the repository at this point in the history
Summary:
It's starting locking down on these types, so that we can remove it in the next next release.

Changelog: [errors] Use of `React$ComponentType`, `React$Context` and `React$RefSetter` will now trigger `internal-type` errors.

Reviewed By: gkz

Differential Revision: D70014990

fbshipit-source-id: 4e73ce37154b6080a181087ee319a93d0f874e36
  • Loading branch information
SamChou19815 authored and facebook-github-bot committed Feb 22, 2025
1 parent 4b1a742 commit 2ebbf8b
Show file tree
Hide file tree
Showing 15 changed files with 88 additions and 88 deletions.
33 changes: 33 additions & 0 deletions src/typing/type_annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,28 @@ module Make (Statement : Statement_sig.S) : Type_annotation_sig.S = struct
)
);
local_generic_type ()
| "React$ComponentType" ->
if not (Context.is_lib_file cx) then
Flow_js_utils.add_output
cx
(Error_message.EInternalType
( loc,
Flow_intermediate_error_types.ReactDollarUtilityTypesWithNonDollarAliases
"ComponentType"
)
);
local_generic_type ()
| "React$Context" ->
if not (Context.is_lib_file cx) then
Flow_js_utils.add_output
cx
(Error_message.EInternalType
( loc,
Flow_intermediate_error_types.ReactDollarUtilityTypesWithNonDollarAliases
"Context"
)
);
local_generic_type ()
| "React$ElementRef" ->
if not (Context.is_lib_file cx) then
Flow_js_utils.add_output
Expand All @@ -1382,6 +1404,17 @@ module Make (Statement : Statement_sig.S) : Type_annotation_sig.S = struct
)
);
local_generic_type ()
| "React$RefSetter" ->
if not (Context.is_lib_file cx) then
Flow_js_utils.add_output
cx
(Error_message.EInternalType
( loc,
Flow_intermediate_error_types.ReactDollarUtilityTypesWithNonDollarAliases
"RefSetter"
)
);
local_generic_type ()
(* other applications with id as head expr *)
| _ -> local_generic_type ()
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ References:
20| var x = f<React.ComponentType<{}>, _>()(foo(Comp));
^^ [1]
test1.js:4:34
4| Component: React$ComponentType<{|...P|}>,
4| Component: React.ComponentType<{|...P|}>,
^^^^^^^^ [2]


Expand Down
4 changes: 2 additions & 2 deletions tests/badly_positioned_unknown_use/test1.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';

declare export function foo<P>(
Component: React$ComponentType<{|...P|}>,
): React$ComponentType<P>;
Component: React.ComponentType<{|...P|}>,
): React.ComponentType<P>;

class Comp extends React.Component<{}, {}> {}

Expand Down
2 changes: 1 addition & 1 deletion tests/implicit_instantiation/ub_order_regression.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from 'react';
declare var Comp: (props: {}) => $FlowFixMe;
class C<X> {}
declare var withStore: <Props: {...}>(
C: React$ComponentType<Props>,
C: React.ComponentType<Props>,
) => C<$Diff<Props, {...}>>;

withStore(Comp) as C<{}>; // okay
4 changes: 3 additions & 1 deletion tests/more_react/react-copy-write.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//@flow
var React = require("react");

import type {ComponentType} from 'react';

export type Recipe<T> = (draft: T, state: $ReadOnly<T>) => void;
export type Mutate<T> = (recipe: Recipe<T>) => void;

Expand All @@ -11,7 +13,7 @@ type ProviderProps<T> = {|
initialState?: T,
|};

export type Provider<T> = React$ComponentType<ProviderProps<T>>;
export type Provider<T> = ComponentType<ProviderProps<T>>;

type GetReturnType = <T, S>((T) => S) => S;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {createContext, useContext} from 'react'

export opaque type Tag = number;

export const Context: React$Context<Tag> = createContext<Tag>(3);
export const Context: React.Context<Tag> = createContext<Tag>(3);

component Foo() {
const context = useContext(Context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Cannot cast `context` to number because `Tag` [1] is incompatible with number [2

References:
opaque.js:5:37
5| export const Context: React$Context<Tag> = createContext<Tag>(3);
5| export const Context: React.Context<Tag> = createContext<Tag>(3);
^^^ [1]
non-local.js:7:14
7| context as number; // ERROR
Expand Down
4 changes: 1 addition & 3 deletions tests/react/contravariant_refsetter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import * as React from 'react';

declare const x: React.RefSetter<number | string>;
(x: React.RefSetter<number>); // OK!
(x: React$RefSetter<number>); // OK!

declare const y: React$RefSetter<number | string>;
declare const y: React.RefSetter<number | string>;
(y: React.RefSetter<number>); // OK!
(y: React$RefSetter<number>); // OK!

(x: React.RefSetter<number | string | boolean>); // ERROR
(y: React.RefSetter<number | string | boolean>); // ERROR
2 changes: 1 addition & 1 deletion tests/react/creatRef.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from 'react';
{
class MyComponent extends React.Component<void> {}

const ref: {current: null | React$ComponentType<MyComponent>} = React.createRef(); // Ok
const ref: {current: null | React.ComponentType<MyComponent>} = React.createRef(); // Ok
}

{
Expand Down
6 changes: 3 additions & 3 deletions tests/react/hoc.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as React from 'react';

function myHOC(
Component: React$ComponentType<{foo: number, bar: number}>,
): React$ComponentType<{foo: number}> {
Component: React.ComponentType<{foo: number, bar: number}>,
): React.ComponentType<{foo: number}> {
return class extends React.Component<{foo: number}, {bar: number}> {
state: {bar: number} = {bar: 2};
render(): React.Node {
Expand Down Expand Up @@ -33,7 +33,7 @@ function UnwrappedFun(props: {foo: number, bar: number}) {
myHOC(class Empty extends React.Component<{foo: string}, void> {}); // Error
myHOC(function Empty(props: {foo: string}) {}); // Error

const Wrapped: React$ComponentType<{foo: number}> = myHOC(Unwrapped);
const Wrapped: React.ComponentType<{foo: number}> = myHOC(Unwrapped);
const WrappedFun = myHOC(UnwrappedFun);

<Wrapped nonsense="what" />; // Error: `foo` is required.
Expand Down
103 changes: 35 additions & 68 deletions tests/react/react.exp
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
Error ---------------------------------------------------------------------------------- contravariant_refsetter.js:11:2
Error ----------------------------------------------------------------------------------- contravariant_refsetter.js:9:2

Cannot cast `x` to `React.RefSetter` because in type argument `T` [1]: [incompatible-cast]
- Either boolean [2] is incompatible with number [3].
- Or boolean [2] is incompatible with string [4].

contravariant_refsetter.js:11:2
11| (x: React.RefSetter<number | string | boolean>); // ERROR
contravariant_refsetter.js:9:2
9| (x: React.RefSetter<number | string | boolean>); // ERROR
^

References:
<BUILTINS>/react.js:357:34
357| declare export type RefSetter<-T> = React$RefSetter<T>;
^ [1]
contravariant_refsetter.js:11:39
11| (x: React.RefSetter<number | string | boolean>); // ERROR
contravariant_refsetter.js:9:39
9| (x: React.RefSetter<number | string | boolean>); // ERROR
^^^^^^^ [2]
contravariant_refsetter.js:3:34
3| declare const x: React.RefSetter<number | string>;
Expand All @@ -23,62 +23,29 @@ References:
^^^^^^ [4]


Error ---------------------------------------------------------------------------------- contravariant_refsetter.js:12:2
Error ---------------------------------------------------------------------------------- contravariant_refsetter.js:10:2

Cannot cast `y` to `React.RefSetter` because: [incompatible-cast]
- Either boolean [1] is incompatible with number [2] in the first parameter.
- Or boolean [1] is incompatible with string [3] in the first parameter.
- Or function type [4] is incompatible with object type [5]. Functions without statics are not compatible with objects.

contravariant_refsetter.js:12:2
12| (y: React.RefSetter<number | string | boolean>); // ERROR
^

References:
contravariant_refsetter.js:12:39
12| (y: React.RefSetter<number | string | boolean>); // ERROR
^^^^^^^ [1]
contravariant_refsetter.js:7:34
7| declare const y: React$RefSetter<number | string>;
^^^^^^ [2]
contravariant_refsetter.js:7:43
7| declare const y: React$RefSetter<number | string>;
^^^^^^ [3]
contravariant_refsetter.js:7:18
7| declare const y: React$RefSetter<number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [4]
<BUILTINS>/react.js:192:5
192| | { -current: T | null, ... }
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [5]


Error ---------------------------------------------------------------------------------- contravariant_refsetter.js:12:2

Cannot cast `y` to `React.RefSetter` because: [incompatible-cast]
- Either boolean [1] is incompatible with number [2] in property `current`.
- Or boolean [1] is incompatible with string [3] in property `current`.
- Or object type [4] is incompatible with function type [5]. Non-callable objects are not compatible with functions.
Cannot cast `y` to `React.RefSetter` because in type argument `T` [1]: [incompatible-cast]
- Either boolean [2] is incompatible with number [3].
- Or boolean [2] is incompatible with string [4].

contravariant_refsetter.js:12:2
12| (y: React.RefSetter<number | string | boolean>); // ERROR
contravariant_refsetter.js:10:2
10| (y: React.RefSetter<number | string | boolean>); // ERROR
^

References:
contravariant_refsetter.js:12:39
12| (y: React.RefSetter<number | string | boolean>); // ERROR
^^^^^^^ [1]
contravariant_refsetter.js:7:34
7| declare const y: React$RefSetter<number | string>;
^^^^^^ [2]
contravariant_refsetter.js:7:43
7| declare const y: React$RefSetter<number | string>;
^^^^^^ [3]
contravariant_refsetter.js:7:18
7| declare const y: React$RefSetter<number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [4]
<BUILTINS>/react.js:193:6
193| | ((T | null) => mixed)
^^^^^^^^^^^^^^^^^^^ [5]
<BUILTINS>/react.js:357:34
357| declare export type RefSetter<-T> = React$RefSetter<T>;
^ [1]
contravariant_refsetter.js:10:39
10| (y: React.RefSetter<number | string | boolean>); // ERROR
^^^^^^^ [2]
contravariant_refsetter.js:6:34
6| declare const y: React.RefSetter<number | string>;
^^^^^^ [3]
contravariant_refsetter.js:6:43
6| declare const y: React.RefSetter<number | string>;
^^^^^^ [4]


Error ------------------------------------------------------------------------------------------- createContext.js:16:24
Expand Down Expand Up @@ -575,7 +542,7 @@ References:
9| <Component />; // Error: `foo` is required.
^^^^^^^^^^^^^ [1]
hoc.js:4:34
4| Component: React$ComponentType<{foo: number, bar: number}>,
4| Component: React.ComponentType<{foo: number, bar: number}>,
^^^^^^^^^^^^^^^^^^^^^^^^^^ [2]


Expand All @@ -593,7 +560,7 @@ References:
9| <Component />; // Error: `foo` is required.
^^^^^^^^^^^^^ [1]
hoc.js:4:34
4| Component: React$ComponentType<{foo: number, bar: number}>,
4| Component: React.ComponentType<{foo: number, bar: number}>,
^^^^^^^^^^^^^^^^^^^^^^^^^^ [2]


Expand All @@ -611,7 +578,7 @@ References:
10| <Component foo={42} />; // Error: `bar` is required.
^^^^^^^^^^^^^^^^^^^^^^ [1]
hoc.js:4:34
4| Component: React$ComponentType<{foo: number, bar: number}>,
4| Component: React.ComponentType<{foo: number, bar: number}>,
^^^^^^^^^^^^^^^^^^^^^^^^^^ [2]


Expand All @@ -626,7 +593,7 @@ property `foo`. [incompatible-call]

References:
hoc.js:4:40
4| Component: React$ComponentType<{foo: number, bar: number}>,
4| Component: React.ComponentType<{foo: number, bar: number}>,
^^^^^^ [2]


Expand All @@ -641,7 +608,7 @@ Cannot call `myHOC` with function bound to `Component` because number [1] is inc

References:
hoc.js:4:40
4| Component: React$ComponentType<{foo: number, bar: number}>,
4| Component: React.ComponentType<{foo: number, bar: number}>,
^^^^^^ [1]
hoc.js:34:35
34| myHOC(function Empty(props: {foo: string}) {}); // Error
Expand All @@ -662,7 +629,7 @@ References:
39| <Wrapped nonsense="what" />; // Error: `foo` is required.
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1]
hoc.js:36:36
36| const Wrapped: React$ComponentType<{foo: number}> = myHOC(Unwrapped);
36| const Wrapped: React.ComponentType<{foo: number}> = myHOC(Unwrapped);
^^^^^^^^^^^^^ [2]


Expand All @@ -680,7 +647,7 @@ References:
41| <WrappedFun />; // Error: `foo` is required.
^^^^^^^^^^^^^^ [1]
hoc.js:5:24
5| ): React$ComponentType<{foo: number}> {
5| ): React.ComponentType<{foo: number}> {
^^^^^^^^^^^^^ [2]


Expand Down Expand Up @@ -1655,12 +1622,12 @@ Cannot call `React.createContext` with `'hello'` bound to `defaultValue` because
`CustomType` [2]. [incompatible-call]

useContext_hook.js:21:73
21| const InvalidContext: React$Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType
21| const InvalidContext: React.Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType
^^^^^^^ [1]

References:
useContext_hook.js:21:39
21| const InvalidContext: React$Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType
21| const InvalidContext: React.Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType
^^^^^^^^^^ [2]


Expand All @@ -1675,7 +1642,7 @@ Cannot assign `React.useContext(...)` to `stringValue` because `CustomType` [1]

References:
useContext_hook.js:23:38
23| const CustomContext: React$Context<CustomType> = React.createContext({
23| const CustomContext: React.Context<CustomType> = React.createContext({
^^^^^^^^^^ [1]
useContext_hook.js:13:20
13| let stringValue: string;
Expand Down Expand Up @@ -2422,7 +2389,7 @@ Cannot assign `use(...)` to `notAny` because `Theme` [1] is incompatible with nu

References:
use_hook.js:4:43
4| declare const ThemeContext: React$Context<Theme>;
4| declare const ThemeContext: React.Context<Theme>;
^^^^^ [1]
use_hook.js:19:17
19| const notAny: 0 = use(ThemeContext);
Expand Down Expand Up @@ -2543,7 +2510,7 @@ References:



Found 145 errors
Found 144 errors

Only showing the most relevant union/intersection branches.
To see all branches, re-run Flow with --show-all-branches
4 changes: 2 additions & 2 deletions tests/react/useContext_hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ type CustomType = {|
stringValue = React.useContext(StringContext); // Ok
numericValue = React.useContext(StringContext); // Error: string is incompatible with number

const InvalidContext: React$Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType
const InvalidContext: React.Context<CustomType> = React.createContext('hello'); // Error: inexact string is incompatible with exact CustomType

const CustomContext: React$Context<CustomType> = React.createContext({
const CustomContext: React.Context<CustomType> = React.createContext({
foo: 'abc',
bar: 123,
});
Expand Down
2 changes: 1 addition & 1 deletion tests/react/use_hook.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {use} from 'react';

declare opaque type Theme;
declare const ThemeContext: React$Context<Theme>;
declare const ThemeContext: React.Context<Theme>;

declare const AnswerPromise: Promise<42>;

Expand Down
2 changes: 1 addition & 1 deletion tests/spread/error_positions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type F<T> = (React.ElementConfig<T> => void) => void // error should not appear

declare function foo<T>(T): (React.ElementConfig<T> => void) => void

declare function bar<P>(React$ComponentType<{ m: number, ...P}>): React$ComponentType<P>;
declare function bar<P>(React.ComponentType<{ m: number, ...P}>): React.ComponentType<P>;

class C extends React.Component<{}> {}

Expand Down
Loading

0 comments on commit 2ebbf8b

Please sign in to comment.