skip to Main Content

I have a boolean variable (let’s call it isX) and two functions (getA and getB) that return different types of array:

const isX: boolean; (value unknown at compilation time)
fn getA(): A[];
fn getB(): B[];

The function that is called depends on the boolean variable, following by some common logic that doesn’t depend on type and then finally mapping the elements to components depending on the boolean variable value (either ComponentA or componentB)

const data: A[] | B[] = isX ? getA() : getB();
... custom logic common for both types of elements ...
return data.map(el => isX ? <ComponentA c={el} /> : <ComponentB c={el} />)

Last line gives me the following error:

Type 'A | B' is not assignable to type 'A'.
   Type 'B' is missing the following properties from type 'A': x, y, and 3 more.

What would be the best solution to solve this problem?

2

Answers


  1. The error you’re encountering is due to TypeScript’s type checking. Since you’re using conditional logic to determine whether to call getA() or getB(), TypeScript can’t definitively infer the type of data as either A[] or B[], resulting in the type error when you try to map over it.

    To solve this issue:

    const data: (A | B)[] = isX ? getA() : getB();
    // ... custom logic common for both types of elements ...
    return data.map(el => isX ? <ComponentA c={el as A} /> : <ComponentB c={el as B} />);
    

    By using type assertions (el as A and el as B), you’re telling TypeScript that even though data might contain elements of type A | B, you’re treating them as specific types A and B respectively inside the map function.

    Login or Signup to reply.
  2. While this can be solved by asserting the type (el as A) it’s probably not a very good solution.

    A popular concept for situations like yours is Discriminated Unions. By runtime checking for a property in a Union Type TypeScript can narrow the type to either A or B. Usually one uses a specific property that is available on all types of the union but technically it also works like this:

    type A = {x: string};
    type B = {y: string};
    
    const ComponentA = (props: A) => 0;
    const ComponentB = (props: B) => 0;
    
    declare function getA(): A[];
    declare function getB(): A[];
    declare const isX: boolean;
    
    let data: (A | B)[] = isX ? getA() : getB();
    
    data.map(el => {
      if ("x" in el) { // <- We know that "x" is only available in type A
        return ComponentA(el);
      } else {
        return ComponentB(el);
      }
    });
    

    TypeScript Playground

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