6

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?

4
  • It depends; it's not really clear what you're trying to achieve, because the example you show doesn't do anything. But you could use type guards, see e.g. typescriptlang.org/docs/handbook/advanced-types.html Commented May 28, 2018 at 7:26
  • @jonrsharpe ectus already answered my question. Going to use type asserting (as you mentioned). Thats the clearest solution in my case. Commented May 28, 2018 at 14:45
  • Note that will only work in the case when all of the Items' product properties are Products, not strings, in which case allowing it in the original type seems pointless. Commented May 28, 2018 at 14:59
  • Its obvious. Initial reason to have multiple types was to check types and through error. Would radically simplify debugging in the future. Yet thank you for pointing it out Commented May 28, 2018 at 16:13

3 Answers 3

5

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
})
Sign up to request clarification or add additional context in comments.

1 Comment

@Timothy asserting, not casting. If one of those is products is actually a string at runtime, the code will break (which is what the compiler's telling you to start with).
0

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 }}

4 Comments

The reason I use it - is because this value literally might be either a string or an object. Depending whether MongoDB ObjectID has been populated with referenced object or not. In some callbacks it is is some not..
then again the question is why can't you represent your string value as product instance? If the fields of Product is are only name & id, I think the better approach is setting the type of id to number | null and always create product instances
No its not.. I am very much simplified the situation. The Product object is huge. Yet thank you for your answer.
K but then again, maybe your specific use case is to complex to describe here, I don't see how your product is either 1 String or an entire object with dozens of properties. I mean typesafety does not help you at all since you have to treat them completely different. And if you have to treat them differently it often does not make sense to have it in the same variable
-1

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.