Timothy
Timothy

Reputation: 3593

How to properly handle TypeScript values with multiple types

I have following TS interface

export interface Item {
  product: string | Product;
}

When I want to literate through array of items I have to eliminate the type. In other words

items = Items[];
items.forEach(item => {
  item.product._id
})

is not going to work because property is is not appliable to strings. Hence I have to pre-check the type, i.e.:

items = Items[];
items.forEach(item => {
  if (typeof item.product === 'object') item.product._id
})

I don't really like how it looks. How do you handle this situation?

Upvotes: 6

Views: 10475

Answers (3)

Nicolas Gehlert
Nicolas Gehlert

Reputation: 3253

This may sound weird but the first/best approach should be "don't have a class variable with mixed type string | product".

What is your specific use case? Why are there some products as strings and some as classes? Is it possible to create an instance for the string classes as well?

If this is somehow not possible for some reasons, you can check with instanceof for the proper type (do not check with typeof === 'object', as it is not really safe)

if (item instanceof Product) { // do logic }}

Upvotes: 0

Estus Flask
Estus Flask

Reputation: 222309

There should be type guards in order to narrow down the type:

if (item.product && typeof item.product === 'object') item.product._id

Or:

if (item.product && typeof item.product !== 'string') item.product._id

If there's a possibility that item.product is a string as the interface suggests, this check is needed.

Otherwise the type should be asserted:

items.forEach(item => {
  (item.product as Product)._id
})

Upvotes: 5

user4676340
user4676340

Reputation:

Javascript doesn't type variables, so I imagine your issue is only about your IDE.

The first and most obvious solution is to declare your variable as any. No further issue, but you won't have autocomplete.

Second solution, not practical, is to create a typed variable :

if (item instanceof Product) {
  _item: Product = item;
  // Now you have autocomplete on _item
} else if (typeof item === 'string') {
  _item: string = item;
  // Same here. 
}

This isn't practical because you will have to write 2 times your logic : once for the product, once for the string. But ! it may be not practical, but it is exhaustive, since you have to implement both logic (and they surely differ because you don't treat a string like an object, and vice versa).

Upvotes: -1

Related Questions