Typescript Issue: From "or" to "specific" back to "or" (ReactJS example)

Viewed 55

I guess the hardest part about this question was to find a suitable title

So actually what I try to achieve is to render specific slides - but still.. every slide.. is actually a slide by data

lets look at what I mean and what I get from the api

type SomeSlideType = {
  type: 'SomeSlide';
  label: string;
  headline: string
}
type SomeOtherSlideType = {
  type: 'SomeOtherSlide';
  members: number;
  totalViews: number;
}
type AndAnotherSlideType = {
  type: 'AndAnotherSlide';
  someProp: string
  someOtherProp: number;
}
type SlideType = {
  sys: { id: string };
  nextSlide: { id: string };
  slide: SomeSlideType | SomeOtherSlideType | AndAnotherSlideType;
};

and in the end I get an array of slides => SlideType[]

So actually what I want to do is render them and still get the comfort of typesupport within the slides What I NOT want to have, is some big chunk of if else cluster like that:

export const Example = ({ slides }: { slides: SlideType[] }) => (
  <div>
    {
      slides.map(slide => (
        <>
          {
            slide.slide.type === "SomeSlide" ? (
              <SomeSlide slideData={slide} />
            ) : slide.slide.type === "SomeOtherSlide" ? (
              <SomeOtherSlide slideData={slide} />
            ) : slide.slide.type === "AndAnotherSlide" ? (
              <AndAnotherSlide slideData={slide} />
            ) : null
          }
        </>
      ))
    }
  </div>
)

This way I would get the specific slide type info, but as there are many different slides (and many more to come) I'd love to go for an more dynamic approach like that:

const SLIDES_MAP: Record<
  SlideType["slide"]["type"],
  ((props: SlideType) => JSX.Element)
> = {
  SomeSlide: SomeSlideComponent,
  SomeOtherSlide: SomeOtherSlideComponent,
  AndAnotherSlide: AndAnotherSlideComponent
};


const Slide = ({ slideData }: ISlideProps) => {
  const RenderSlide = SLIDES_MAP[slideData.slide.type];

  if (!RenderSlide) return null;

  return (
    <RenderSlide
      slide={slideData.slide}
      sys={slideData.sys}
      nextSlide={slideData.nextSlide}
    />
  );
};


export const Example = ({ slides }: { slides: SlideType[] }) => (
  <div>
    {
      slides.map(slide => (
        <Slide slideData={slide} />
      ))
    }
  </div>
)

With that a slide itself would look something like that:

const SomeSlideComponent = ({
  slide,
  nextSlide,
  sys,
}: SlideType & { slide: SomeSlideType }) => ( // <====== interesting part
    <h1>
      {slide.headline}
    </h1>
  );


export default SomeSlideComponent;

Of course it now complains, that the return type of SomeSlideComponent doesn't match with ((props: SlideType) => JSX.Element) as we manipulated the type to SlideType & { slide: SomeSlideType } to specify it (and SomeSlideType !== SomeSlideType | SomeOtherSlideType | AndAnotherSlideType

Still, to come to the MAIN question, with SlideType & { slide: SomeSlideType } I thought somehow I could define it as a valid "value" as I defined the SlideType earlier with:

type SlideType = {
  sys: { id: string };
  nextSlide: { id: string };
  slide: SomeSlideType | SomeOtherSlideType | AndAnotherSlideType;
};

so SomeSlideType is actually a valid option for slide

anyone has an idea how to tackle this? some TS trick? I tried to use Partial<XYZ> too, but maybe I'm just to unskilled with that

I really hope I could pin down the issue in an understandable way, otherwise please feel free to request a more specific description on something :)

Thank you and cheers!

0 Answers
Related