Home > Enterprise >  Typescript return union "either or" throws property not found error
Typescript return union "either or" throws property not found error

Time:05-25

I'm trying to write a Typescript helper function that returns a valid either/or type but I'm not understanding how to appease the compiler. My types are defined as follows:

export interface MediaElementUrl {
  media_type: 'image' | 'video';
  url: string;
  attachment_id: never;
  buttons?: Button[];
}

export interface MediaElementAttachmentId {
  media_type: 'image' | 'video';
  url: never;
  attachment_id: string;
  buttons?: Button[];
}

export type MediaElement = MediaElementUrl | MediaElementAttachmentId;

You can have a MediaElement that has a url or a attachment_id but not both. My helper class is as follows:

export class MediaElementComponent {
  protected constructor(
    public media_type: 'image' | 'video',
    public button: Button,
    public attachment_id?: string,
    public url?: string
  ) {}

  public validate(): void {
    if (this.attachment_id && this.url) throw new Error('Cannot set url and attachment_id');
  }

  public get(): MediaElement {
    if (this.attachment_id) {
      return {
        buttons: [this.button],
        media_type: this.media_type,
        attachment_id: this.attachment_id!,
      };
    }
    return {
      buttons: [this.button],
      media_type: this.media_type,
      url: this.url!,
    };
  }
}

The compiler is throwing an error on the return from get()

TS2322: Type '{ buttons: Button[]; media_type: "image" | "video"; attachment_id: string; }' is not assignable to type 'MediaElement'.   Property 'url' is missing in type '{ buttons: Button[]; media_type: "image" | "video"; attachment_id: string; }' but required in type 'MediaElementAttachmentId'.

Can anyone help me understand where I'm going wrong? Thanks

CodePudding user response:

Simple fix: You need to make the forbidden properties optional:

export interface MediaElementUrl {
  media_type: 'image' | 'video';
  url: string;
  attachment_id?: never;
  buttons?: Button[];
}

export interface MediaElementAttachmentId {
  media_type: 'image' | 'video';
  url?: never;
  attachment_id: string;
  buttons?: Button[];
}

Otherwise TypeScript will enforce that these properties are present with a type of never, which is impossible.

Playground

  • Related