What is preventing typescript from inferring these linked types automatically?

Consider the following code snippet:

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'
}

What prevents TypeScript from recognizing that data always exists when flag is set to true?

In this scenario, it would be beneficial if TypeScript could automatically infer the existence of data without requiring an explicit return type definition for the function.

Answer №1

To achieve this, you have two options: use as const or implement a discriminated union for your success property.

Example 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);
}

Example 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);
}

The reason behind this issue is that when simply assigning true or false, TypeScript assumes the property to be of type boolean. Therefore, in the case of data: null, it cannot guarantee that { success: true, data: null } will not occur. Additionally, there's no type information linking flag to the return type, which is where generics can be beneficial.

Answer №2

Perhaps this solution will work:

interface ResponseSuccess {
  success: true
  data: { name: string; age: number }
}
interface ResponseFailure {
  success: false
  data: null
}
const bar = <Flag extends boolean, Result extends Flag extends true ? ResponseSuccess : ResponseFailure>(flag: Flag): Result {
  if (flag) {
    return {
       success: true,
       data: {
         name: "Alice",
         age: 30
       }
    } as Result 
  }

  return {
    success: false,
    data: null
  } as Result 
}    

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Organize a list in AngularJS by breaking it down based on its headers and displaying them in

As someone who is new to angularJs, I am looking to convert an app to angularJs but I have encountered a roadblock: The custom accordion markup I am working with is as follows: <div class="accord_main_wrap" ng-controller="catController"> <di ...

Using jQuery functions to disable adding or removing classes with a click event

Has anyone had success implementing a functionality where a div expands upon clicking, and reverts back to its original state when clicking on a cancel link within the same div? <div class="new-discussion small"> <a class="cancel">Cancel&l ...

typescript import { node } from types

Exploring the possibilities with an electron application developed in typescript. The main focus is on finding the appropriate approach for importing an external module. Here is my typescript configuration: { "compilerOptions": { "target": "es6", ...

Tips for safely storing JWT tokens in a react/next.js app:

After a valid user login following proper registration through REST API, I am looking for the best way to store the JWT token that is generated. I have researched various methods of storing JWT on the client side, including local storage, session storage, ...

The property fails to reflect changes in state

I am currently developing an application that requires fetching data asynchronously and preserving the state in the parent component while also passing the data reference to children components. I encountered an issue where the props do not update when the ...

The drawbacks of using toJS() in React Redux: Is it really necessary in mapStateToProps()?

After reading through the Redux documentation, I came across a recommendation to not use Immutable with Redux. This advice has left me feeling confused. Why should I avoid using toJS() in the mapStateToProps function? It seems that React already uses Dee ...

Why is the last move of the previous player not displayed in this game of tic tac toe?

Why does the last move of the previous player not show up in this tic-tac-toe game? When it's the final turn, the square remains empty with neither an "O" nor an "X". What could be causing this issue? Here is the code snippet: The HTML code: <div ...

TypeScript's Awaitable concept

Lately, I have been utilizing async / await functions quite frequently in my JavaScript projects. As I make the transition to TypeScript, I am gradually converting some sections of my code bases. In certain scenarios, my functions require a function as an ...

Using a PHP switch case statement with an HTML multiple select dropdown

I am facing an issue with my HTML multiple select box: <option value="1">First choice</option> <option value="2">Second choice</option> <option value="3">Third choice</option> Using jQuery, ...

What is the best way to retrieve the promise that encountered an error in the catch block while using async/await

I'm currently in the process of converting code that used .then/.catch to instead use async/await. One particular challenge I'm facing is how to access the original promise that fails within the catch block, for logging purposes. Here is the ori ...

Leveraging Selenium for extracting data from a webpage containing JavaScript

I am trying to extract data from a Google Scholar page that has a 'show more' button. After researching, I found out that this page is not in HTML format but rather in JavaScript. There are different methods to scrape such pages and I attempted t ...

Exploring the world of jQuery and Ajax to retrieve a full HTML framework

I'm a beginner in jQuery and JavaScript programming. While I've managed to use jQuery for my Ajax calls, I'm facing an issue that has me stumped. When making an Ajax call, I'm trying to return a complete HTML structure, specifically a ...

Use an input of map<string,string> when passing to an angular component

Trying to pass an input value for a component reuse, but facing the challenge of having to use a "hardcoded" map of strings. Uncertain about how to effectively pass this information: <continue-p [additionalInfo]="{ "myString": "str ...

Is there a way to retrieve the Response object in Express from outside the Route

Currently, my backend API is built using Express JS. Instead of using the res object directly in the route controller, I am looking to access it from a custom service. While I know that I can simply pass res as an argument to the service function and use ...

Modifying property values using the onError event when encountering an error with the MUI DateTimePicker and

My goal is to set the 'myError' variable to true when MUI DateTimePicker throws an onError event, but for some reason it's not working. The code itself seems correct because if I replace () => {setValue({ ...value, ...

Navigating through async functions in an Express.js router

Encountered a lint error indicating that Promises cannot be returned in places where a void is expected. Both functions [validateJWT, getUser] are async functions. Any suggestions on how to resolve this issue without compromising the linter guidelines by u ...

What's the process for setting a value in selectize.js using Angular programmatically?

Currently, I am implementing the AngularJS directive to utilize selectize.js from this source: https://github.com/kbanman/selectize-ng In my scenario, I have two dropdowns and my goal is to dynamically populate one of them called selectizeVat based on the ...

Tips for converting file byte code to file form data

From one folder I am retrieving a file and uploading it to another server. However, when I extract the file from the first folder, I receive a byte code representation of that file. The API for uploading the file to the second folder requires FormData as a ...

Send in the completed form with your chosen preferences

Is there a way for me to submit a form with the selected option? My backend code is set up to work when using inputs: <input id="id_value_0" name="value" type="radio" value="1" /> <input id="id_value_1" name="value" type="radio" value="2" /> ...

Provide an immutable parameter to a function that will not cause any changes

Looking to develop a function named batchUsers, requiring a parameter of type readonly string in order to create a DataLoader. However, when calling the User.findBy function within my batchUsers function, it's causing issues due to conflicting paramet ...