Home > database >  How to type narrow JSX.Element
How to type narrow JSX.Element

Time:11-24

I'd like to have a variable that can be either a React component or a string, like this:

function MyComponent(): JSX.Element {
  let icon: JSX.Element | string = "/example.png";  // (simplified)

  return <div>{typeof icon === "JSX.Element" ? icon : <img src={icon} />}</div>;
}

however TS complains about my condition saying:

TS2367: This condition will always return 'false' since the types '"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"' and '"JSX.Element"' have no overlap.

What should I do?

CodePudding user response:

The typeof operator cannot return "JSX.Element". It can only return the string types listed in that error message:

"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"

Which means that you will need to check against something in that list, like "string".

function MyComponent(): JSX.Element {
  let icon: JSX.Element | string = "/example.png";
  
  return <div>{typeof icon === "string" ? <img src={icon} /> : icon }</div>;
}

CodePudding user response:

The accepted answer is the best for my simplified question.

If for some reason, you do want to type narrow against the element, rather than against string, apparently you can also use React.isValidElement, which type narrows.

You'd have to change the type JSX.Element to ReactElement:

function MyComponent(): JSX.Element {
  let icon: ReactElement | string = "/example.png";  // (simplified)

  return <div>{React.isValidElement(icon) ? icon : <img src={icon} />}</div>;
}

If changing to JSX.Element is unacceptable, you could create your own user-defined type guard, which interrogates the object to determine if it is indeed a JSX.Element (see here).

  • Related