PeterN
PeterN

Reputation: 2177

Typescript property does not exist on union type

This is a situation I have ran into a couple of times, it seems like it should be fairly straightforward, but I can't find a solution that doesn't set the type to any

A function takes one of two different objects as the argument, checks which object has been received, and returns the corresponding field.

This is a simplified version of the problem, but the issue is that the two objects are only distinguishable by their properties(which have no overlap), and I can't access any of the properties, because they're not present on the other type.

type Obj1 = {
  message: string
}

type Obj2 = {
  text: string
}

const getText = (obj: Obj1 | Obj2): string => {
  if (obj.message) {
    return obj.message
  }

  return obj.text
}

Upvotes: 194

Views: 66785

Answers (4)

Murat Karagöz
Murat Karagöz

Reputation: 37584

You have to narrow down the type. You can do so by using the in operator.

const getText = (obj: Obj1 | Obj2): string => {
  if ("message" in obj) {
    return obj.message
  }

  return obj.text
}

Upvotes: 343

The real answer to this problem according to what the question owner asked is this

But there might be a time you are using your defined type with primitive type in this way the above solution is not going to work as the problem I faced here is the situation

type Obj1 = {
  message: string
}

const getText = (obj: Obj1 |string): string => {
  if (obj.message) {
    return obj.message
  }

  return obj.text
}

so this scenario the solution stated above would not be perfect for you, so you might need to use typeof ✌️

const getText = (obj: Obj1 | string): string => {
  if (typeof obj !== 'string') {
    return obj.message
  }

  return obj.text
}

Upvotes: 6

Yuki Ito
Yuki Ito

Reputation: 513

I recommend typescript-is.

import { is } from 'typescript-is';

...

const getText = (obj: Obj1 | Obj2): string => {
  if (is<Obj1>(obj)) {
    return obj1.message;
  }

  return obj2.text;
};

Upvotes: 1

nsevens
nsevens

Reputation: 2835

You can cast the object to either Obj1 or Obj2:

type Obj1 = {
  message: string
}

type Obj2 = {
  text: string
}

const getText = (obj: Obj1 | Obj2): string => {
  if ((obj as Obj1).message) {
    return (obj as Obj1).message
  }

  return (obj as Obj2).text
}

Upvotes: 52

Related Questions