1

I have three different interfaces which share the same property and want to create a custom typeguard that will check which of interfaces value from [ value, setValue ] = useState({}) has, and depending on it, run adequate setValue(...value, property: newValue);

To better illustrate the situation I copied code written by captain-yossarian from Either type containing different types question, changed it as suggested in the replies, added a third interface that contains type properties, same as the other two interfaces and added typeguards.

import React from 'react';

interface Animal {
  type: string;
  age: number;
}

interface Cat extends Animal {
  typeOfCat: string;
}

interface Bus {
  type: string;
  model: string;
}

type AnimalOrCatOrBus<T> = T extends any ? {
  value: T;
  setValue: React.Dispatch<React.SetStateAction<T>>;
} : never;

type Props = AnimalOrCatOrBus<Animal | Cat | Bus>;

const ChangeType = (props: Props): JSX.Element => {

  type MergeTypes = Animal | Cat | Bus;

  function isCat(v: MergeTypes): v is Cat {
    return (v as Cat).typeOfCat !== undefined;
  };
  function isAnimal(v: MergeTypes): v is Animal {
    return !isCat(v) && (v as Animal).type !== undefined;
  };
  function isBus(v: MergeTypes): v is Bus {
    return (v as Bus).model !== undefined;
  };

  const { value, setValue } = props;

  const handleChange = (data: string) => {
    if (isAnimal(value)) {
      setValue({ ...value, type: data });
    } else if (isCat(value)) {
      setValue({ ...value, type: data });
    } else if (isBus(value)) {
      setValue({ ...value, type: data });
    }
  };
  return (
    <form>
      <label>
        <input
          type="text"
          value={value.type}
          onChange={(data) => handleChange(data.target.value)}
        />
      </label>
    </form>
  );
};

const jsx = <ChangeType value={{ type: 'animal', age: 42 }} setValue={() => { }}/>;
const jsx2 = <ChangeType value={{ type: 'animal', age: 42, typeOfCat: 'cat' }} setValue={() => { }}/>;
const jsx3 = <ChangeType value={{ type: 'vehicle', model: 'volksvagen' }} setValue={() => { }}/>;

From what I see the typeguards works for value and compiler see that value is Bus, Cat or Animal after putting it through if(isX(value)) function which use type predicates, but setValue stays React.Dispatch<React.SetStateAction<Bus>> & React.Dispatch<React.SetStateAction<Cat>> & React.Dispatch<React.SetStateAction<Animal>> and I don't know how to create such typeguard for setValue for it to know which of T = Animal | Bus | Cat in React.Dispatch<React.SetStateAction<T>> it should expect.

Sandbox

Jueltrae
  • 43
  • 1
  • 8

0 Answers0