/**
* 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
*/
'use strict';
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, ...}) => (
{requiredProp}
);
// 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 (
{reqLen && optionalProp && optionalFoo && missing && defLen}
);
}
}
const Bar = createFragmentContainer(BarComponent, {
viewer2: graphql`
fragment ReactRelayFragmentContainerFlowtest_viewer2 on Viewer {
actor {
id
}
}
`,
});
module.exports = {
checkMissingPropOnFunctionalComponent(): React.Node {
/** $FlowExpectedError: Foo missing `requiredProp` **/
return ;
},
checkMinimalPropsOnFunctionalComponent(): React.Node {
// Fine, no expected errors
return ;
},
checkMissingPropOnClassComponent(): React.Node {
/** $FlowExpectedError: Bar missing `requiredProp` **/
return ;
},
checkMinimalPropsOnClassComponent(): React.Node {
// All is well
return ;
},
checkWrongPropType(): React.Node {
/** $FlowExpectedError: Bar wrong `requiredProp` type, should be string **/
return ;
},
checkWrongOptionalType(): React.Node {
/** $FlowExpectedError: Bar wrong `optionalProp` type, should be `{foo: string}` **/
return ;
},
checkNullOptionalType(): React.Node {
/** $FlowExpectedError: Bar `optionalProp` must be omitted or truthy, not null **/
return ;
},
checkWrongDefaultPropType(): React.Node {
/** $FlowExpectedError: Bar wrong `defaultProp` type, should be string **/
return ;
},
checkAllPossibleProps(): React.Node {
// All is well
return (
);
},
checkMinimalPropSpread(): React.Node {
// All is well
const props = {requiredProp: 'foo'};
return ;
},
checkMissingPropSpread(): React.Node {
const props = {defaultProp: 'foo'};
/** $FlowExpectedError: Bar missing `requiredProp` with spread **/
return ;
},
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 ;
}
}
return ;
},
};