Reputation: 40064
I have a React component whose type is based on a runtime variable (isMock) and having difficulties with getting the TS declarations to work:
The component is either MockedProvider
or ApolloProvider
from @apollo/client
which have two different props declaration.
const isMock: boolean = true // hardcoded for now...
const GraphQLProvider = isMock ? MockedProvider : ApolloProvider
const mockProps = { ... }
const realProps = { ... }
type ProviderType = typeof MockedProvider | typeof ApolloProvider
const GraphQLProvider: ProviderType = isMock ? MockedProvider : ApolloProvider
type ProviderProps = React.ComponentProps<typeof GraphQLProvider>
const graphQLParams: ProviderProps = isMock ? mockProps : realProps
return (
<GraphQLProvider {...graphQLParams}> // Error here...
...
</GraphQLProvider>
)
The above does not work. How would I correctly write declarations to allow for the condition isMock
?
I didn't post the error since its quite verbose but basically it tells me that one or more props is missing even though it's not for the determined component.
Upvotes: 0
Views: 86
Reputation: 963
The main problem is that you have two union types (ProviderType
and ProviderProps
) that don't have any relation to each other. At some point, you have to narrow down the types to use the right props for the corresponding components.
See the following example. There are two components Foo
and Bar
that have completely different props
. The Baz
component is used to render one of them based on the specified props
. Here's the important thing. The BazProps
type is a union type that is narrowed down by the if
statement via Type Guards. I used the in
operator in Baz
and a custom user-defined type guard in AlternativeBaz
.
import React from 'react';
// Foo
interface FooProps {
foo: string;
}
const Foo = ({ foo }: FooProps) => <>Foo {foo}</>;
// Bar
interface BarProps {
bar: number;
}
const Bar = ({ bar }: BarProps) => <>Bar {bar}</>;
// Baz
type BazProps = FooProps | BarProps;
const Baz = (props: BazProps) => {
if ('foo' in props) {
return <Foo {...props} />; // props is FooProps
} else {
return <Bar {...props} />; // props is BarProps
}
};
// Alternative Baz
const isFooProps = (props: BazProps): props is FooProps => 'foo' in props;
const AlternativeBaz = (props: BazProps) => {
if (isFooProps(props)) {
return <Foo {...props} />; // props is FooProps
} else {
return <Bar {...props} />; // props is BarProps
}
};
// App
const fooProps: FooProps = { foo: 'Foo' };
const barProps: BarProps = { bar: 42 };
interface AppProps {
isFoo: boolean;
}
export const App = ({ isFoo }: AppProps) => {
const bazProps = isFoo ? fooProps : barProps;
return (
<>
<Baz {...bazProps} />
<AlternativeBaz {...bazProps} />
</>
);
};
Upvotes: 1