Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

props.items (Object) is possibly undefined after return early condition

I've seen a few similar questions but none had the same issue.

I am being confused around Object that is possibly undefined error from TypeScript.

interface Props {
  item?: SomeItem;
}

export const Element = (props: Props) => {
  if (props.item === undefined) {
    return null;
  }

  const offer = offers.find((offer) => offer.id === props.item.offer_id);

  return <div>{props.item.getPayoutPlaceholder()}</div>; 
};

The error is

TS18048: 'props.item' is possibly 'undefined'. const offer = offers.find((offer) => offer.id === this.item.offer_id);

It shows ONLY in the find line. The last one (return) doesn't suffer from this.

If I assign the props.item to a constant and use it, It stops

  const item = props.item;
  if (item === undefined) {
    return null;
  }

  const offer = offers.find((offer) => offer.id === item.offer_id);

I get that it could change after the if by some asynchronous code. But what makes me really confused is the fact that it doesn't show the same error in return statement. What could be the reason for this?

like image 665
Martin. Avatar asked Oct 20 '25 04:10

Martin.


1 Answers

This is because props.item.offer_id is in a callback, and so TS cannot be sure that props.item has not changed between the time you check it and the time the callback is called. As you noted, you could get around it by defining a constant variable:

const item = props.item;

It should also be noted that destructuring also works:

export const Element = ({ item }: Props) => {

but notice that changing const to let makes the original error surface again:

let item = props.item;

and that's because now TS is not sure that it hasn't changed, like before (since let is mutable and can be reassigned).

The usage of props.item.offer_id works in the return statement because it isn't in any sort of context that can have delayed execution. As an example of this, in the following code, TS is smart enough to see that this function expression will be immediately invoked and thus it must be synchronous:

// OK, even though it's in another function
(() => props.item.offer_id)();

Anyways, regardless of why TS behaves the way it does here, I suggest you use destructuring directly from the parameter list. It's clean, widely used, and solves this issue straight away without needing to declare extra constant variables.

like image 81
catgirlkelly Avatar answered Oct 23 '25 01:10

catgirlkelly