Reputation: 1086
In Typescript+React. I've got two components (modules) with specific types specified with an interface. In addition I've got one parent component which takes an array with a union specified as props.
The child components:
Module One
interface IModuleOneProps {
moduleOneSpecificProp: string;
module_type: sting;
commonProp: string;
}
const ModuleOne: React.SFC<IModuleOneProps> = props => {
return (
<p>{moduleOneSpecificProp + " " + commonProp}</p>
);
};
export default ModuleOne;
Module Two
interface IModuleTwoProps {
moduleTwoSpecificProp: string;
module_type: sting;
commonProp: string;
}
const ModuleTwo: React.SFC<IModuleTwoProps> = props => {
return (
<p>{moduleTwoSpecificProp + " " + commonProp}</p>
);
};
export default ModuleTwo;
The container component which contains an array of modules, which can be of both types:
interface IContainerModuleProps {
modules: Array<
IModuleOneProps | IModuleTwoProps
>;
}
class ContainerModule extends React.Component<IContainerModuleProps> {
public render() {
const module = this.props.modules[1];
switch (module.module_type) {
case 'module_one':
return <ModuleOne {...module} />;
case 'module_two':
return <ModuelTwo {...module} />;
}
}
}
But TypeScript won't let me spread module
when rendering the components. It complains about the types.
Type '{ commonProp: string; moduleOneSpecificProp: string; }' is not assignable to type 'IntrinsicAttributes & IModuleOneProps & { children?: ReactNode; }'.
Type '{ commonProp: string; moduleTwoSpecificProp: string; }' is not assignable to type 'IntrinsicAttributes & IModuleOneProps & { children?: ReactNode; }'.
Property 'moduleOneSpecificProp' is missing in type '{ commonProp: string; moduleTwoSpecificProp: string; }'.
Is there a quick fix I'm not seeing here? Or do I have to rebuild the prop object (module
) before passing it down to the child component?
Upvotes: 1
Views: 1396
Reputation: 249536
You need to define the module_type
property as a string literal type (a string type that an have only one value) if you do so, then the switch
will type guard correctly:
interface IModuleOneProps {
moduleOneSpecificProp: string;
commonProp: string;
module_type: "module_one"
}
interface IModuleTwoProps {
moduleTwoSpecificProp: string;
commonProp: string;
module_type: "module_two"
}
interface IContainerModuleProps {
modules: Array<
IModuleOneProps | IModuleTwoProps
>;
}
public render() {
const module = this.props.modules[1];
switch (module.module_type) {
case 'module_one':
return <ModuleOne {...module} />;
case 'module_two':
return <ModuleTwo {...module} />;
}
}
It is also perfectly possible to use an enum for the string literals to avoid being dependent on magic strings:
enum MODULE_NAME {
MODULE_ONE,
MODULE_TWO
}
And then use MODULE_NAME.MODULE_ONE
on both the interface and in the render method.
Upvotes: 1