When using the new way of typing HOC's in v0.53.0, it is required that all injected properties are present in the Props definition of the Component that is being wrapped. For instance, not having bar in Props for MyComponent causes an error in this example
Some HOC's inject quite a few properties into a component, but I may only care about utilizing one or two of them. Is it possible to create a type definition for an HOC that does not require the user to include every prop that is injected?
I _think_ this is the same problem as in #4644. The solution there will probably work here too.
At least one difference is that setting the props as optional in the HOC definition does not seem to fix it as it does in #4644. Example.
I get why it's an error; the HOC definition is saying that the input component should have an optional bar prop and the component I'm passing doesn't have a bar prop specified. Just sure would be convenient if there was a way to specify _if it has these props, they are this type, but it may only have a subset of them and that is okay_.
Oh! Feels like something that could be solved with $Shape.
Yeah, that was one of the first things I tried because it did seem appropriate, but I get the same error using $Shape as well. Example
// @flow
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<$Shape<{ foo: number, bar: number }> & Props>,
): React.ComponentType<Props> {
return function WrapperComponent(props: Props) {
return <Component {...props} foo={42} bar={100} />;
};
}
class MyComponent extends React.Component<{
a: number,
b: number,
foo: number,
}> {}
const MyEnhancedComponent = injectProp(MyComponent);
// We don't need to pass in `foo` even though `MyComponent` requires it.
<MyEnhancedComponent a={1} b={2} />;
If I make a small change to that example, by replacing the intersection type with a type spread, it works:
// @flow
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<{ foo: number, bar?: number, ...Props }>,
): React.ComponentType<Props> {
return function WrapperComponent(props: Props) {
return <Component {...props} foo={42} bar={100} />;
};
}
class MyComponent extends React.Component<{
a: number,
b: number,
foo: number,
}> {}
const MyEnhancedComponent = injectProp(MyComponent);
// We don't need to pass in `foo` even though `MyComponent` requires it.
<MyEnhancedComponent a={1} b={2} />;
Let me know if that handles what you're looking for.
That does appear to work; bar doesn't even need to be optional in the HOC definition (example). Thanks! Not quite sure I understand why this works vs intersection, but I'm pretty unaware of the differences between type spread and intersection in general.
It does appear that there are some negative consequences to this approach though
// @flow
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<{ foo: number, bar: number, ...Props }>,
): React.ComponentType<Props> {
return function WrapperComponent(props: Props) {
return <Component {...props} foo={42} bar="100" />;
};
}
class MyComponent extends React.Component<{
a: number,
b: number,
foo: number,
bar: number,
}> {}
const MyEnhancedComponent = injectProp(MyComponent);
// We don't need to pass in `foo` even though `MyComponent` requires it.
<MyEnhancedComponent a={1} b={2} />;
I am now able to pass a string in the HOC for bar and it doesn't catch the type error. (link)
I believe this has been fixed since I filed the issue. I haven't had issues typing HOC's since React.ElementConfig was added: https://flow.org/en/docs/react/hoc/#toc-supporting-defaultprops-with-react-elementconfig
Most helpful comment
It does appear that there are some negative consequences to this approach though
I am now able to pass a string in the HOC for bar and it doesn't catch the type error. (link)