skip to Main Content

New to react typescript and I have been getting below typescript error while creating components:

interface APIResponseA {
a:string[];
b:number;
c: string | null; // <-
}

interface APIResponseB {
a:string[] | null;
b:number;
d:number; // <-
e:string; // <-
}

interface IProps {
details: APIResponseA | APIResponseB;
} 

...
const values: IValues = {
a:props.details.a || [];
b:props.details.b || 0;
c:props.details?.c || ""; // <- ts error
d:props.details?.d || 0; // <- ts error
e:props.details?.e || ""; // <- ts error
}

getting error for c, d, e

Property 'd' does not exist on type 'APIResponseA | APIResponseB'.
  Property 'd' does not exist on type 'APIResponseA'.ts(2339)

Trying to reuse the component so need it to be dynamic.

The issue seems like here details: APIResponseA | APIResponseB; but not sure how to correctly set the type. Please help?


Adding some context:
This component is a form (using formik) in which the initial values are populated from API responses. Basically it’s the same form but for different pages the API response varies.

2

Answers


  1. This is expected as the details property is the union of ResponseA and ResponseB. So typescript can only guarentee the types that are both in ResponseA and ResponseB.

    You can use the in operator in typescript to narrow it’s type. You can do something like this. Take a look at this typescript playground

    type ResponseA = {
      a: number;
      b: number;
    };
    
    type ResponseB = {
      a: number;
      c: number;
      d: number;
    };
    
    type IProps = {
      details: ResponseA | ResponseB;
    };
    
    const Component = (props: IProps) => {
      if ("c" in props.details) {
        console.log(props.details); // details is of type ResponseB
      }
    
      if ("b" in props.details) {
        console.log(props.details); // details is of type ResponseA
      }
    };
    
    
    Login or Signup to reply.
  2. Well it’s hard to know what’s the best solution without knowing the context.

    Typically you will transform the response of those APIs to a data type that fit into a component. It seems like your component is just the representation of those data. So this is how I will do in this case:

    // Define the final prop type for this component, which c, d, e might be undefined depend on different API responses
    interface IProps {
      details: {
        a:string[] | null;
        b:number;
        c?: string | null;
        d?: number;
        e?:string;
      }
    } 
    
    function Foo(props: IProps) {
      return (
        <>
          <IPropsChildComponent>{props.details.a}</IPropsChildComponent>
          <div>{props.details.b}</div>
          {/* the following could be empty so you might need to handle this in UI */}
          <div>{props.details.c}</div>
          <div>{props.details.d}</div>
          <div>{props.details.e}</div>
        </>
      )
    }
    

    APIResponseA | APIResponseB will result an interface that has no c, d, e fields.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search