I'm using flow types and have a memo wrapped functional component, however when trying to assign navigationOptions for react-navigation, flow is just going haywire:
On my component
const navigationOptions = ({navigation}) => ({}) // more stuff
const Foo = memo((props: IProps) => {
...
})
/*
Here it breaks down
Cannot assign `navigationOptions` to `Foo['navigation...']` because property `navigationOptions` is missing in `React.AbstractComponentStatics`
*/
Foo.navigationOptions = navigationOptions
export default Foo
Even if I type cast it to any, and then try to use it on my router:
/*
Cannot call `createStackNavigator` with object literal bound to `routeConfigMap` because in property `Foo.screen`: Either property `navigationOptions` is missing in AbstractComponent [1] but exists in `withOptionalNavigationOptions` [2]. Or property `router` is missing in AbstractComponent [1] but exists in `withRouter` [3].Flow(InferError)
*/
const FooStack = createStackNavigator({
Foo: { screen: FooScreen }
}, stackNavOptions)
How am I supposed to properly apply types here?
This is an expected behaviour. If you actually check React$AbstractComponentStatics you'll see that there are very few allowed properties, such as displayName which is valid. They've even removed propTypes which even though it's valid it's not encouraged which is why it has been removed.
To get around this you can use a suppression comments to mark it as a false positive,
// $FlowExpectedError navigationOptions
Foo.navigationOptions = navigationOptions
Flow supports $FlowFixMe and as of version 0.125.0, $FlowIssue and $FlowExpectedError by default to suppress errors.
Related
I have a functional component below which has props coming from parent component and I added propTypes for the whole props object. However, the lint fails with the below error message.
9:16 error 'data' is missing in props validation react/prop-types
9:22 error 'data.text' is missing in props validation
Excerpt from code
import PropTypes from 'prop-types'
const Child = (props) => <div>{props.data.text}</div>
Child.propTypes = {
props: PropTypes.object
}
Could anyone please help?
As discussed in the comments, you have a few issues:
PropTypes.object is a very vague type declaration, and most linters will ding you for that
You are referencing properties of that object inside of your functional component that are not declared in your proptypes
You are using your prop declaration to attempt to refer to the props argument as a whole, rather than the properties within.
A more correct way to write all of this would be like so:
import PropTypes from 'prop-types';
/* Note we destructure `data` directly from props in the args */
const Child = ({ data }) => (<div>{data.text}</div>);
Child.propTypes = {
data: PropTypes.shape({ // use `shape` to allow us to declare types of properties within
text: PropTypes.string, // define the text property's type
}),
}
Additionally, you may want to define some of these items as required, or provide default entries.
From Alexander Nied solution, I made it work writing the below way without using shape, I think it is more generic.
const Child = ({ data }) => (<div>{data.text}</div>);
Child.propTypes = {
data: PropTypes.object
}
I have a simple React Component which I already added inline Flow type:
src/MyComponent.jsx
// #flow
type MyComponentProps = {
id: number,
name: string
}
const MyComponent = ({ id, name}: MyComponentProps) =>
<div>{`My name is ${name} and ${id} is my ID.`}</div>
export (MyComponent: MyComponentProps => React$Node)
The problem is the component gets a little dirty with the inline annotations and when you implement the component, the IDE tooltip is not really clear about what properties it can take:
The best I could do was changing the export of MyComponent specifying the props one by one. This makes the IDE Tooltip clearer:
export default (MyComponent: ({ name: string, id: number }) => React$Node)
But the code gets even messier.
I saw that you can create a Declaration File and move the typing there, but I just can't make it work and couldn't find an example for React Function Component.
This is what I have, but this doesn't work.
src/MyComponent.jsx.flow
// #flow
declare module 'MyComponent' {
declare type MyComponentProps = {
id: number,
name: string
}
declare export function MyComponent(props: MyComponentProps): React$Node}
Can you help me to move the type to the external file definition, please?
Thanks in advance.
Ideally you shouldn't be trying to move your definitions to another file. Writing them inline has the advantage of making the actual component type checked with the props it accepts as opposed to only for the components using this MyComponent.
Have you tried this? Which moves the annotation position to where the component is defined. Which means if you ever add JSDocs they will also appear correctly in your IDE.
// #flow
type MyComponentProps = {
id: number,
name: string
}
/**
* It allows you to comment like this
*/
const MyComponent = ({ id, name}: MyComponentProps): React$Node =>
<div>{`My name is ${name} and ${id} is my ID.`}</div>
export MyComponent
I'd also say that code being messy or clean is quite subjective, though I feel the same way about annotation on the export looks "messy". So the above example is how I generally write my components.
Say I have the following condition:
static propTypes = {
deployment: PropTypes.shape({
getMetrics: PropTypes.func.isRequired,
getRootMetric: PropTypes.func.isRequired
}).isRequired,
}
How would I declare the default for deployment ? Would it be undefined or would it be [] ?
Since the prop is required, I see no reason to define a default. Defaults are only useful for optional props which also have a reasonable default value, such as an empty list [] or an empty object {}.
You could not define a default prop or you could just say {getMetrics: () => {}, getRootMetric: () => {}} since it's an object. However, required props don't really need defaults, otherwise they wouldn't really be required.
To give an example :
You build a small npm module that comes along with a config object
You can define this object as required (the module would break
without users providing it)
You can provide defaults (it would work with these if not provided)
So, finally, it's up to you if and how you provide defaults.
I would use something like the below:
SomeComponent.defaultProps = {
deployment: {
getMetrics: () => {},
getRootMetric: () => {}
}
}
Defaults are good when not having the values would break your app.
I have a problem: My IDE is complaining about deprecation.
I use react 16 and typescript 2.5.3
Following code given:
import React, { Props, StatelessComponent } from 'react';
import PropTypes from 'prop-types';
interface IProps {
id: string;
}
const Foo: StatelessComponent<IProps> = (props: IProps) => {
props.ref = nodeRef;
return (<div id={props.id}></div>);
};
Foo.propTypes = {
id: PropTypes.string,
};
Foo.defaultProps = {
id: 'bar',
};
export default Foo;
At this point I am getting at props.ref 'Property ref does not exist on Partial'
When I extend my interface IProps, like this:
interface IProps extends Props {
id: string;
}
At this point my IDE suggest to add an Generic Type
interface IProps extends Props<any> {
id: string;
}
Now I get deprecation warning to consult online docs, BUT I do not find anything. BUT my initial error with ref-property is gone.
Now my question: How to deal with this when I use a StatelessComponent? How to deal with it, when Using Component (or is there no error)? And how can I avoid any?
Thanks for helping me
You've accidentally masked the real problem by extending Props<T>! There's a comment in the type definitions explaining why that interface is deprecated:
This was used to allow clients to pass ref and key to createElement, which is no longer necessary due to intersection types.
In other words, you used to have to extend Props<T> for your prop type definitions so that ref/key/children would be included, but new features in TypeScript have made this unnecessary. You can just pass in a plain interface as you were doing initially.
However, this still leaves you with your 'Property ref does not exist' error - this is because you cannot use refs on stateless functional components. The type definitions are actually doing the right thing here and stopping you from writing code that won't work!
This is specific to a React app I am converting from standard ES2015 to Typescript. In my .tsx files (converted from regular .js files) I am getting a couple of typescript errors I do not know how to solve.
The first is: error TS2339: Property 'propTypes' does not exist on type 'typeof App'.
This applies to:
App.propTypes = {
actions: PropTypes.object.isRequired,
fuelSavingsAppState: PropTypes.object.isRequired
};
I tried creating an interface above that code that defined App like:
interface App {
propTypes?: any;
}
But this had no effect. Clearly I am new to Typescript so what am I missing?
The other error is:
error TS2605: JSX element type 'Element' is not a constructor function for JSX elements.
Property 'render' is missing in type 'Element'
and this applies to:
{settings.necessaryDataIsProvidedToCalculateSavings ? <FuelSavingsResults savings={settings.savings} /> : null}
The error specifically refers to the bit about <FuelSavingsResults
At a complete loss as to where to even start with that. Any suggestions are most welcome.
in case it helps, the definition for FuelSavingsResults starts like:
let FuelSavingsResults = (props) => {
let savingsExist = NumberFormatter.scrubFormatting(props.savings.monthly) > 0;
let savingsClass = savingsExist ? 'savings' : 'loss';
let resultLabel = savingsExist ? 'Savings' : 'Loss';
and then has a regular React render method that follows, but I don't think that will matter.
error TS2339: Property 'propTypes' does not exist on type 'typeof App'.
Instead of
class App extends React.Component{
}
App.propTypes = {
actions: PropTypes.object.isRequired,
fuelSavingsAppState: PropTypes.object.isRequired
};
Use a static property.
class App extends React.Component{
static propTypes = {
actions: PropTypes.object.isRequired,
fuelSavingsAppState: PropTypes.object.isRequired
};
}
error TS2605: JSX element type 'Element' is not a constructor function for JSX elements.
Property 'render' is missing in type 'Element'
This was a bug in the compiler that has since been fixed : https://github.com/Microsoft/TypeScript/issues/6349 Alternative provide type annotations for props. 🌹
Is propTypes really required? Won't interface check at compile time what propTypes check in runtime?
interface IApp{
actions:any;
fuelSavingsAppState: (props:any)=> void;
}
class App extends React.Component<IApp,{}>{
}
or if you are using dumb components
const App = ({actions, fuelSavingsAppState }:IApp):React.ReactElement<{}> => (
<div>
What Ever you want to do with the props
</div>
)
Note: If you are using object please create an interface for the same. Like actions as of now given as any which should be ideally an interface.