TypeScript: How to tell TypeScript in which If case I am?

893 views Asked by At

I'm building a React Native application using TypeScript. My startup recently switched to TypeScript from JavaScript and I'm migrating the code.

I have a <FlatList /> that consists of two types of venues: Restaurant and Bar. Restaurant and Bar share some properties, but they also don't. For example the property servesFood is unique to Restaurants.

The renderItem function looks like this:

renderItem = ({ item }: { item: Restaurant | Bar }) => {
  return item.servesFood ? (
    // ... Restaurant code
  ) : (
    // ... Bar code
  )

The problem is, that in the condition for this ternary operator TypeScript throws the error:

Property 'servesFood' does not exist on type 'Restaurant | Bar'.
  Property 'servesFood' does not exist on type 'Bar'

Furthermore in the type specific code when accessing type specific properties there are also linting errors.

Note: For various reason I can't let them share a property and set one to true on the first one and to false on the other one.

So how can I tell TypeScript that in the one part of the If clause / ternary operator item is of type Restaurant and in the other item is of type bar, so that these linting errors go away.

2

There are 2 answers

0
Titian Cernicova-Dragomir On BEST ANSWER

You can use a type guard to narrow the type of the parameter.

You can use discriminated unions based on the servesFood field:

interface Restaurant{
    servesFood: true
}
interface Bar {
  servesFood?: false
}
const renderItem = ({ item }: { item: Restaurant | Bar }) => {
      return item.servesFood ? (
        // ... Restaurant code
      ) : (
        // ... Bar code
      )

Or if the interfaces do not share servesFood you can use an in type guard

interface Restaurant{
    servesFood: true
}
interface Bar {
  drinks: true
}
const renderItem = ({ item }: { item: Restaurant | Bar }) => {
      return 'servesFood' in item ? (
        item.servesFood
      ) : (
        // ... Bar code
        item.drinks
      );
0
Brank Victoria On

You can do a simple compare the key/value of the object so you can know if the property servesFood exists for the current item. If the vaule is null it means that item doesn't contains the property servesFood Something like this:

renderItem = ({ item }: { item: Restaurant | Bar }) => {
  return ((item["servesFood"] != null) ? (
    // ... Restaurant code, it exists the property servesFood
  ) : (
    // ... Bar code, it doesn't exists the property servesFood so it is null
  ))