0

I have a type defined that looks like this:

export type MediaProps = ImageMediaProps | OembedProps;

Then have the types it's referencing defined as the following above that code:

type SharedMediaProps = {
  /** Media type */
  type: "image" | "oembed";
  /** Media source URL */
  src: string;
};

type ImageMediaProps = SharedMediaProps & {
  /** Image alternate text */
  alt: string;
  /** Image width */
  width: number;
  /** Image height */
  height: number;
};

type OembedProps = SharedMediaProps & {
  /** Enable video autoplay */
  autoplay?: boolean;
  /** Enable video loop */
  loop?: boolean;
  /** Allow fullscreen */
  allowFullscreen?: boolean;
  /** Allow picture-in-picture */
  allowPictureInPicture?: boolean;
  /** oEmbed title */
  title?: string;
};

Then in my React component, I have:

export function Media({
  type,
  title,
  width,
  height,
  src,
  autoplay = false,
  loop = false,
  allowFullscreen = true,
  allowPictureInPicture = true,
}: MediaProps) {

but I'm getting notices saying title, width, height, autoplay, loop, allowFullscreen, allowPictureInPicture aren't defined.

For example, the specific notice I'm getting is:

Property 'allowFullscreen' does not exist on type 'MediaProps'.ts(2339)

It's also happening on other components I've created.

3
  • 2
    I don't see allowFullscreen anywhere in your type definitions Commented Apr 20, 2022 at 14:38
  • 1
    Or OembedProps at all. Please give a minimal reproducible example. Commented Apr 20, 2022 at 14:39
  • Whoops, sorry about that. I've updated it, didn't get the full copy and paste. @TobiasS. Commented Apr 20, 2022 at 14:42

2 Answers 2

2

First, move the type property into the not shared props, so it can be used to discrimitate the type:

type SharedMediaProps = {
   /** Media source URL */
   src: string;
};

type ImageMediaProps = SharedMediaProps & {
   /** Media type */
   type: "image";
   /** Image alternate text */
   alt: string;
   /** Image width */
   width: number;
   /** Image height */
   height: number;
};

type OembedProps = SharedMediaProps & {
   /** Media type */
   type: "oembed";
   /** Enable video autoplay */
   autoplay?: boolean;
   /** Enable video loop */
   loop?: boolean;
   /** Allow fullscreen */
   allowFullscreen?: boolean;
   /** Allow picture-in-picture */
   allowPictureInPicture?: boolean;
   /** oEmbed title */
   title?: string;
};

Then define MediaProps as union of those types:

type MediaProps = ImageMediaProps | OembedProps;

After all this you still can't deconstruct the property in the paramenter because you have to discriminate the type first:

export function Media(props: MediaProps) {
   if(props.type === "image") {
      const {alt, width, height} = props;
   } else {
      const {autoplay, loop, allowFullscreen, allowPictureInPicture, title} = props;
   }
}

Demo: https://tsplay.dev/NBkVnm

Sign up to request clarification or add additional context in comments.

1 Comment

Beautiful, even better, thanks!
1

I believe you'll need to use an intersection type on your MediaProps assignment.

export type MediaProps = ImageMediaProps & OembedProps;

3 Comments

Bingo! That did it, thanks!
But this way MediaProps has ALWAYS both the attributes of ImageMediaProps and OembedProps types. Don't you want to have ONLY the ImageMediaProps when type is image and ONLY the OembedProps when the type is oembed? if so check my answer
I think what you're proposing is a better solution @coglialoro but itself is outside the scope of the original ask and requires refactoring export function Media arguments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.