0

I have code that is something like this

// some package
export interface TCustomer {
  name: string;
}
import { TCustomer } from "some-package"

interface BCustomer extends TCustomer {
  options: string;
}

type Props = {
  customers: TCustomer[] | BCustomer[];
};

const CustomersTable: React.FC<Props> = ({ customers }) => {

  return customers.map(customer => (
    <div>
      {customer.options && <span>{customer.name}</span>}
      <span>{customer.name}</span>
    </div>
  )
)}

Typescript doesn't like that customer.options doesn't exist on the imported type of TCustomer. That makes sense to me and is true. What I don't understand is why? I'm handling the "type" by using && so my intuition is that TS should see that I know that this "might" be undefined.

What is typescript trying to enforce here, and how can I write an if statement on a union type in a more Typescript way?

I'm aware that I "could" write:

interface BCustomer extends TCustomer {
  options: string;
}
interface CCustomer extends TCustomer {
  options?: undefined;
}

type Props = {
  customers: CCustomer[] | BCustomer[];
};

But this seems unneccesary when I could handle the this internally to the component. It seems odd to add a field to a type that shouldn't have that field. However, this may be the "correct" way to do it. If that's the case that's fine. If that's the case I would like to understand why TS is this way so that I can write better TS.

1 Answer 1

1

You can't directly access a property if TypeScript can't determine that the property definitely exists. Even if you're only accessing it to check for truthyness, it's not allowed, unfortunately. Here, you can use in instead. (Using in is not only permitted, it'll also let TS narrow the type for use later - while not useful here since you aren't using any BCustomer-specific properties, it's a good technique to know.)

{'options' in customer && <span>{customer.name}</span>}

Since you only need JavaScript logic and not TypeScript narrowing here, you could also use

{customer.hasOwnProperty('options') && <span>{customer.name}</span>}

Even if {customer.options && was permitted, it might be a mistake, because not all customers with a falsey options will be a TCustomer - it might be a BCustomer with an empty string.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. This seems like a reasonable solution. I'm not quite sure I like this but I'll get over it. Appreciate your time!

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.