SergioP
SergioP

Reputation: 608

Typescript type checking with a mixed member types

I'm working with Giphy's API with Typescript and the response give me a mixed media array of objects, something I can summarize like this:

interface Type {
  width: number;
  height: number;
}

interface Image extends Type {
  url: string;
}

interface Video extends Type {
  videoUrl: string;
}

interface Media {
  typeA: Image;
  typeB: Image & Video;
}

I created a getUrl function that allows the user to select the url setting the media type (typeA or typeB) and the url type (url or videoUrl) but, as you can see, not all the combinations are valid (I can't select videoUrl for typeA media).

const getUrl = (media, mediaType: 'typeA' | 'typeB', mediaUrl: 'url', 'videoUrl'): string => {
  return media[mediaType][mediaUrl];
};

This is facing me some typescript errors, like:

Element implicitly has an 'any' type because expression of type '"url" | "videoUrl"' can't be used to index type 'Image | (Image & Video)'. Property 'videoUrl' does not exist on type 'Image | (Image & Video)'.ts(7053)

Can someone help me to improve the type checking and fixing the errors?

Link to codesandbox

Upvotes: 0

Views: 287

Answers (2)

You can slightly change Props interface:

import * as React from "react";

interface Type {
  width: number;
  height: number;
}

interface PropsA {
  mediaType: 'typeA';
  url: "url";
}

interface PropsB {
  mediaType: 'typeB';
  mediaUrl: 'videoUrl';
}

type Props = PropsA | PropsB


interface Image extends Type {
  url: string;
}

interface Video extends Type {
  videoUrl: string;
}

interface Media {
  typeA: Image;
  typeB: Image & Video;
}

const media: Media = {
  typeA: {
    width: 100,
    height: 100,
    url: "my_url_01"
  },
  typeB: {
    width: 100,
    height: 100,
    url: "my_url_02",
    videoUrl: "my_video_url_02"
  }
}

const MyComponent = (props: Props) => {
  const getUrl = () => {

    if (props.mediaType === 'typeA') {
      const x = props; // PropsA
      const { mediaType, url } = props;
      return media[mediaType][url]//[mediaType][mediaUrl];
    }
    const x = props; // PropsB
    const { mediaType, mediaUrl } = props;
    return media[mediaType][mediaUrl];

  };

  return <div>{getUrl()}</div>;
};

Upvotes: 1

ABOS
ABOS

Reputation: 3823

In order for typescript to distinguish union types, you need to type guard it. Can you try the following?

if(mediaType==='typeA') {
  return 'something';
} else {
  return media[mediaType][mediaUrl];
}

Upvotes: 0

Related Questions