Reputation: 14748
I'm building a React app with TypeScript. I have a RequiresPermission
component, that based on a predicate, should render one or the other component and forward all props.
type Props = {
NotPermittedComponent: React.ComponentType;
PermittedComponent: React.ComponentType;
isPermitted: boolean;
};
const RequiresPermisson = ({
NotPermittedComponent,
PermittedComponent,
isPermitted,
...rest
}: Props) =>
isPermitted ? (
<PermittedComponent {...rest} />
) : (
<NotPermittedComponent {...rest} />
);
export default RequiresPermisson;
When I render the component, TypeScript yells about RequiresPermission
in:
const PERMITTED_TEXT = 'permitted';
const NOT_PERMITTED_TEXT = 'not-permitted';
type TestPropsProps = {
text: string;
};
const NotPermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
text,
}) => (
<div>
<span>{NOT_PERMITTED_TEXT}</span>
{text}
</div>
);
const PermittedTestComponent: React.FunctionComponent<TestPropsProps> = ({
text,
}) => (
<div>
<span>{PERMITTED_TEXT}</span>
{text}
</div>
);
const createProps = ({
NotPermittedComponent = NotPermittedTestComponent,
PermittedComponent = PermittedTestComponent,
isPermitted = false,
text = 'foo',
} = {}) => ({
NotPermittedComponent,
PermittedComponent,
isPermitted,
text,
});
const props = createProps();
render(<RequiresPermission {...props} />);
saying:
Type '{ NotPermittedComponent: FunctionComponent<TestPropsProps>; PermittedComponent: FunctionComponent<TestPropsProps>; isPermitted: boolean; text: string; }' is not assignable to type '{ NotPermittedComponent: ComponentType<{}>; PermittedComponent: ComponentType<{}>; isPermitted: boolean; }'.
Types of property 'NotPermittedComponent' are incompatible.
Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'ComponentType<{}>'.
Type 'FunctionComponent<TestPropsProps>' is not assignable to type 'FunctionComponent<{}>'.
Types of parameters 'props' and 'props' are incompatible.
Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren<TestPropsProps>'.
Property 'text' is missing in type '{ children?: ReactNode; }' but required in type 'TestPropsProps'.
I also tried Record<string, unknown>
as props
but that doesn't work either.
How can you fix this to either pass the props or allow any (not the type any
) props so the ...rest
parameter works?
Upvotes: 4
Views: 4097
Reputation: 1171
Not sure if this is the most elegant solution, but I have just combined all the prop types. This gives you type checking for all the props. I have to forward all the props because I can not make sure something like isPermitted
is not required on a sub component.
interface Props <A, B>{
NotPermittedComponent: React.ComponentType<A>;
PermittedComponent: React.ComponentType<B>;
isPermitted: boolean;
}
const RequiresPermisson = <A, B>(props: Props<A, B> & A & B): JSX.Element => {
const {
NotPermittedComponent,
PermittedComponent,
isPermitted,
} = props
return isPermitted ? (
<PermittedComponent {...props} />
) : (
<NotPermittedComponent {...props} />
)
}
Upvotes: 2
Reputation: 10412
You need generic types from typescript: https://www.typescriptlang.org/docs/handbook/generics.html
In short, these are dynamic types that depends on what is inputted.
In the code below, we assign all props that are passed to the type T, and tell typescript that the props are: required props NotPermittedComponent
PermittedComponent
isPermitted
along with all of the rest
, which in this case becomes the type T.
type Props = {
NotPermittedComponent: React.ComponentType;
PermittedComponent: React.ComponentType;
isPermitted: boolean;
};
const RequiresPermisson = <T extends Record<string, unknown>>({
NotPermittedComponent,
PermittedComponent,
isPermitted,
...rest
}: Props & T) =>
isPermitted ? (
<PermittedComponent {...rest} />
) : (
<NotPermittedComponent {...rest} />
);
export default RequiresPermisson;
Upvotes: 0