Nikolai
Nikolai

Reputation: 672

How can I get the value of a property of type multiple union interface types?

I have the following code:

  const arrayFromForm: IFirst[] | ISecond[] | IThird[]; // = ... it's initialized
  const arrayDB: IFirst[] | ISecond[] | IThird[]; // = ... it's initialized
  const entitiesFromDb = arrayDB.filter(
    (entity: IFirst | ISecond | IThird) =>
      !(arrayFromForm.map(({ id }) => id))
      .includes(entity.id)
  );

And it gives me 1 error: "The map expression is not callable".

Correct me if I'm wrong, but I think that this is because it doesn't know that there is a property named id in all of the interfaces in the union.


I've used a type guard before, but I think you can create a type guard only between 2 types in an union, because you have to return a boolean?

I want to solve this problem with typescript (user-defined type guard, typeof, instanceof, etc.), but I'm not sure that this is possible, isn't it? instanceof type guards can't be created for interfaces, right?
What's a good solution here?


EDIT: I've just solved the problem with an interface with the properties that the interfaces in the union have in common and I just changed the type union with the new interface. This is not very good solution as I would want to use specific methods/properties in the different interfaces that I would've had in the type union.

So I'm not deleting the question, because it's still helpful to know if there is a such thing as an user-defined type guards for type unions with more than 2 types in them.

Upvotes: 2

Views: 376

Answers (2)

Eyad Arafat
Eyad Arafat

Reputation: 611

This seems like a known issue in Typescript. See Here.

But for now you can do this:

const arrayFromForm: IFirst[] | ISecond[] | IThird[]; // = ... it's initialized
const entitiesFromDb = arrayDB.filter(
  (entity: IFirst | ISecond | IThird) =>
    !(arrayFromForm as {id: number}[]).map(item => item.id).includes(entity.id) // Pay attention to the brackets
);

We cast the arrayFromForm as just one appropriate type, instead of a union of types. This is close to the solution you came up with but I think it's easier to maintain it this way.

Upvotes: 1

Aakash Garg
Aakash Garg

Reputation: 10979

Try this :-

const elementsIds = arrayFromForm.filter(arrItem => arrItem.id).map((item)=>item.id); 
  const entitiesFromDb = arrayDB.filter(
    (entity: IFirst | ISecond | IThird) =>
      !elementsIds.includes(entity.id)
  )

Upvotes: 0

Related Questions