I'm creating a website where the user can do some exercises, which can be different React components.
An exercise has a base look (title and completion date) that I have in a wrapper class. The actual component will be passed from the outside:
const exercises = [
<Exercise
key={1}
numberInLesson={1}
title={"Typing the alphabet"}
ExerciseComponent={TypingTheAlphabet}
exerciseComponentProps={{
...some props to pass to TypingTheAlphabet
}}
/>,
<Exercise
key={2}
numberInLesson={2}
title={"Which letter? - Vowels"}
ExerciseComponent={WhichLetter}
exerciseComponentProps={{
...some props to pass to WhichLetter
}}
Now my question is, how can I type exerciseComponentProps in Exercise? e.g. if I pass TypingTheAlphabet
as ExerciseComponent, how can I ensure only props for TypingTheAlphabet
are valid?
Also, I'd like to have a prop onMarkAsCompleted
passed from Exercise to the actual component.
For completeness, here is the props interface of Exercise
and what I want to accomplish:
export interface ExerciseComponentProps {
onMarkAsCompleted: () => void;
}
export type ExerciseComponentType = ComponentType<ExerciseComponentProps>;
export interface CommonExerciseProps<C extends ExerciseComponentType> {
id: string;
numberInLesson: number;
title: string;
ExerciseComponent: C;
exerciseComponentProps: // how can I type this?
}
const Exercise: React.FC<CommonExerciseProps<any>> = (
props
) => {
const [exerciseCompletionDate, setExerciseCompletionDate] =
useState<Date | null>(null);
const {
numberInLesson,
title,
ExerciseComponent,
exerciseComponentProps,
} = props;
return (
<div
className="w3-padding-small"
style={exerciseCompletionDate ? { border: "1px solid green" } : {}}
>
<h3>
Exercise {numberInLesson}:
<br />
{title}
</h3>
{exerciseCompletionDate && (
<small>
Completed on {exerciseCompletionDate.toISOString().substring(0, 10)}
</small>
)}
<ExerciseComponent
onMarkAsCompleted={
!exerciseCompletionDate
? () => setExerciseCompletionDate(new Date())
: () => {}
}
{...exerciseComponentProps}
/>
</div>
);
};
export default Exercise;
CodePudding user response:
export interface CommonExerciseProps<C extends ExerciseComponentType> {
id: string;
numberInLesson: number;
title: string;
ExerciseComponent: C;
exerciseComponentProps: React.ComponentProps<C>
}
React.ComponentProps<typeof Component>