Home > Software design >  How to conditionally call different optional function props in a React TypeScript component without
How to conditionally call different optional function props in a React TypeScript component without

Time:11-22

I have a React TypeScript component which accepts multiple props of which some are optional and these include functions as well. Below is a rough example of what my actual code looks like

interface AnimalProps {
  animalName: string;
  bark?: () => void;
  swim?: () => void;
}

const Animal = ({ animalName, bark, swim }: AnimalProps) => {
  /* some rendering here */
}

I also call the bark or swim function depending upon the animalName. The issue is since both of these are optional props, typescript keeps throwing the error Cannot invoke an object which is possibly undefined. How can I assert to the compiler that at least one of these function will definitely be passed depending upon the animalName ?

CodePudding user response:

You can simply check if the function exists before calling it.

if (animalName === 'dog' && typeof bark === 'function') {
  bark();
}

If you feel like that is too verbose, you can also take advantage of optional-chaining to do the same thing.

if (animalName === 'dog') {
  bark?.(); // Will only invoke bark() if it's defined
}

Optional-chaining is supported in TypeScript since version 3.7.

CodePudding user response:

Apart from the suggested solutions, you may also use discriminating unions: link to the doc which is designed specifically to solve this kind of situations (refer the doc).

like this:

type DogProps = {
  animalName: "dog";
  bark: () => void;
}

type FishProps = {
  animalName: "fish";
  swim: () => void;
}

type AnimalProps = DogProps | FishProps;

const Animal = (props: AnimalProps) => {
  if(props.animalName === 'dog') {
      props.bark();
  } else {
      props.swim();
  }
}

TS Playground link: https://tsplay.dev/NddryN


Destructing isn't possible until you narrow down the type like this (you see other props resolve appropriately without hassle of checking):

type DogProps = {
  animalName: "dog";
  bark: () => void;
  dogProp: string;
}

type FishProps = {
  animalName: "fish";
  swim: () => void;
  fishProp: number;
}

type AnimalProps = DogProps | FishProps;

const Animal1 = (props: AnimalProps) => {
  if(props.animalName === 'dog') {
     const { bark, dogProp } = props;
      bark();
  } else {
    const {swim, fishProp} = props;
      swim();
  }
}

see this: https://tsplay.dev/N9pn7w

  • Related