skip to Main Content

Having the following code:

const foo = (flag: boolean) => {
  if (flag) {
    return {
       success: true,
       data: {
         name: "John",
         age: 40
       }
    }
  }

  return {
    success: false,
    data: null
  }
}    

const result = foo(true);

if (result.success) {
   console.log(result.data.name); // TS error: 'result.data' is possibly 'null'
}

Why can’t typescript infer that data always exists if flag is set to true?

I know I can fix this by defining a return type for the function, but it’d be good if TS automatically infers it.

2

Answers


  1. maybe this code fix:

    interface ReturnSuccess {
      success: true
      data: { name: string; age: number }
    }
    interface ReturnFailure {
      success: false
      data: null
    }
    const foo = <Flag extends boolean, Return extends Flag extends true ? ReturnSuccess : ReturnFailure>(flag: Flag): Return {
      if (flag) {
        return {
           success: true,
           data: {
             name: "John",
             age: 40
           }
        } as Return 
      }
    
      return {
        success: false,
        data: null
      } as Return 
    }    
    
    
    Login or Signup to reply.
  2. You can do this with as const or create a discriminated union around your success property.

    Using as const:

    const foo = (flag: boolean) => {
      if (flag) {
        return {
           success: true,
           data: {
             name: "John",
             age: 40
           }
        } as const; // here
      }
    
      return {
        success: false,
        data: null
      } as const; // here
    }    
    
    const result = foo(true);
    
    if (result.success) {
       console.log(result.data.name);
    }
    

    Using a union:

    type Successful = {
       success: true;
       data: { name: string, age: number }
    }
    
    type Failed {
       success: false;
       data: null;
    }
    
    type Result = Successful | Failed;
    
    function foo(flag: boolean): Result {
      if (flag) {
        return {
           success: true,
           data: {
             name: "John",
             age: 40
           }
        };
      }
    
      return {
        success: false,
        data: null
      };
    }    
    
    const result = foo(true);
    
    if (result.success) {
       console.log(result.data.name);
    }
    
    

    Why this happens is that just assigning true or false, TypeScript infers that property to a boolean so the data: null case, it can’t prove that { success: true, data: null } won’t happen. There’s also no type information that flag has to relate to the return type. That is where generics help.

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