TobiDevloft
TobiDevloft

Reputation: 301

How do I prevent assign errors when using union types in typescript?

Imagine I have these interfaces:

   interface FirstElement {
      id: string;
      coordinates: Coordinates;
      type: FirstElementTypes;
      options: FirstElementOptions;
    }

   interface SecondElement {
      id: string;
      coordinates: Coordinates;
      type: SecondElementTypes;
      options: SecondElementOptions;
    }

    interface FirstElementDTO {
      id: string;
      type: FirstElemenTypes;
      options: FirstElementOptions;
    }

    interface SecondElementDTO {
      id: string;
      type: SecondElementTypes;
      options: SecondElementOptions;
    }

Now I want to create a DTO object, mapping these interfaces to their corresponding DTO interface. Because they share the same properties, with only the options being different, I'd like to use a single converter function. But when using typescripts union typing like this...

  private static convertElementToDTO(element: FirstElement | SecondElement): FirstElementDTO | SecondElementDTO {
    return {
      id: element.id,
      options: element.options,
      type: element.type
    };
  }

...I (obviously) get an error message telling me that the options are not compatible. Would it be possible in typescript to "tell" a function that if FirstElement is the input type, FirstElementDTO is the output type and vice versa for SecondElement - without writing the same code multiple times and using several if-statements? Thanks for your help!

Upvotes: 1

Views: 692

Answers (1)

jcalz
jcalz

Reputation: 327774

It's possible a generic function might work here. First, some dummy type definitions to make the code here a self-contained example:

type FirstElementTypes = { FirstElementTypes: true };
type FirstElementOptions = { FirstElementOptions: true };
type SecondElementTypes = { SecondElementTypes: true };
type SecondElementOptions = { SecondElementOptions: true };

Then we can represent your types this way, to reduce code duplication:

// your types
interface FirstElementDTO {
  id: string;
  type: FirstElementTypes;
  options: FirstElementOptions;
}

interface SecondElementDTO {
  id: string;
  type: SecondElementTypes;
  options: SecondElementOptions;
}

// define elements as extension of DTOs to reduce code duplication
interface FirstElement extends FirstElementDTO {
    coordinates: Coordinates;
}

interface SecondElement extends SecondElementDTO {
    coordinates: Coordinates;
}

And finally here's the generic function:

// generic Pick function
function convertElementToDTO<E extends FirstElement | SecondElement>(
  element: E
): Pick<E, "id" | "options" | "type"> {
  return {
    id: element.id,
    options: element.options,
    type: element.type
  };
}

And it works without error:

declare const f: FirstElementTypes;
declare const o: FirstElementOptions;
declare const c: Coordinates;
const first: FirstElementDTO = convertElementToDTO({
  id: "firstEl",
  type: f,
  coordinates: c,
  options: o
}); // okay

Playground link

Hope that helps; good luck!

Upvotes: 2

Related Questions