react-relay
Version:
A framework for building GraphQL-driven React applications.
154 lines (141 loc) • 4.59 kB
Flow
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall relay
*/
;
const {
createContainer: createFragmentContainer,
} = require('../ReactRelayFragmentContainer');
const React = require('react');
const {graphql} = require('relay-runtime');
/**
* Verifies that normal prop type checking, as well as the methods proxying Relay does, is
* type-checked correctly on Relay components.
*/
const FooComponent = ({requiredProp}: {requiredProp: string, ...}) => (
<div>{requiredProp}</div>
);
// Note that we must reassign to a new identifier to make sure flow doesn't propogate types without
// the relay type definition doing the work.
const Foo = createFragmentContainer(FooComponent, {
viewer: graphql`
fragment ReactRelayFragmentContainerFlowtest_viewer on Viewer {
actor {
id
}
}
`,
});
class BarComponent extends React.Component<{
optionalProp?: {foo: number, ...},
defaultProp: string,
requiredProp: string,
...
}> {
static defaultProps: {defaultProp: string} = {
defaultProp: 'default',
};
getNum(): number {
return 42;
}
render(): React.MixedElement {
const reqLen = this.props.requiredProp.length;
const optionalProp = this.props.optionalProp;
/** $FlowExpectedError: `optionalProp` might be null **/
const optionalFoo = this.props.optionalProp.foo;
/** $FlowExpectedError: there is no prop `missingProp` **/
const missing = this.props.missingProp;
const defLen = this.props.defaultProp.length; // always a valid string, so no error
return (
<div>{reqLen && optionalProp && optionalFoo && missing && defLen}</div>
);
}
}
const Bar = createFragmentContainer(BarComponent, {
viewer2: graphql`
fragment ReactRelayFragmentContainerFlowtest_viewer2 on Viewer {
actor {
id
}
}
`,
});
module.exports = {
checkMissingPropOnFunctionalComponent(): React.Node {
/** $FlowExpectedError: Foo missing `requiredProp` **/
return <Foo />;
},
checkMinimalPropsOnFunctionalComponent(): React.Node {
// Fine, no expected errors
return <Foo requiredProp="foo" />;
},
checkMissingPropOnClassComponent(): React.Node {
/** $FlowExpectedError: Bar missing `requiredProp` **/
return <Bar />;
},
checkMinimalPropsOnClassComponent(): React.Node {
// All is well
return <Bar requiredProp="foo" />;
},
checkWrongPropType(): React.Node {
/** $FlowExpectedError: Bar wrong `requiredProp` type, should be string **/
return <Bar requiredProp={17} />;
},
checkWrongOptionalType(): React.Node {
/** $FlowExpectedError: Bar wrong `optionalProp` type, should be `{foo: string}` **/
return <Bar optionalProp="wrongType" requiredProp="foo" />;
},
checkNullOptionalType(): React.Node {
/** $FlowExpectedError: Bar `optionalProp` must be omitted or truthy, not null **/
return <Bar optionalProp={null} requiredProp="foo" />;
},
checkWrongDefaultPropType(): React.Node {
/** $FlowExpectedError: Bar wrong `defaultProp` type, should be string **/
return <Bar defaultProp={false} requiredProp="foo" />;
},
checkAllPossibleProps(): React.Node {
// All is well
return (
<Bar defaultProp="bar" optionalProp={{foo: 42}} requiredProp="foo" />
);
},
checkMinimalPropSpread(): React.Node {
// All is well
const props = {requiredProp: 'foo'};
return <Bar {...props} />;
},
checkMissingPropSpread(): React.Node {
const props = {defaultProp: 'foo'};
/** $FlowExpectedError: Bar missing `requiredProp` with spread **/
return <Bar {...props} />;
},
checkStaticsAndMethodsProxying(): React.Node {
class ProxyChecker extends React.PureComponent<{}> {
_barRef: ?BarComponent;
getString(): string {
const ok = this._barRef ? this._barRef.getNum() : 'default'; // legit
/** $FlowExpectedError: Bar does not have `missingMethod` **/
const bad = this._barRef ? this._barRef.missingMethod() : 'default';
/** $FlowExpectedError: Bar `getNum` gives number, but `getString` assumes string **/
return bad ? 'not good' : ok;
}
render(): React.MixedElement {
return (
<Bar
componentRef={(ref: empty) => {
this._barRef = (ref: empty);
}}
requiredProp="bar"
/>
);
}
}
return <ProxyChecker />;
},
};