Custom Type Guard Leads to Intersection Type Outcome

Recently, I've been experimenting with Typescript and decided to explore the creation of an innovative Either type that could distinguish between success and failure scenarios. Utilizing a user-defined type guard, I managed to precisely narrow down the types of success and failure using a Symbol. However, I am puzzled as to why the value of either in the event of a failure is displayed as the intersection type of Data & CustomError instead of just CustomError.

const FailureSymbol = Symbol('Failure');

interface IFailure {
    symbol: Symbol;
}

type Success<T> = T
type Failure<T extends IFailure> = T

type Either<T, U extends IFailure> = Success<T> | Failure<U>

function isFailure<T, U extends IFailure>(either: Either<T, U>): either is Failure<U> {
    const candidateFailure = either as Failure<U>;
    return candidateFailure.symbol && candidateFailure.symbol === FailureSymbol;
}

interface Data {
    data: string;
}

interface CustomFailure {
    symbol: Symbol;
    errorMessage: string;
}

let either: Either<Data, CustomFailure> = { data: 'success' }; 

if (isFailure<Data, CustomFailure>(either)) {
    // within this branch, the type of either appears to be: Data & CustomFailure
    console.log('failure', either);
} else {
    // within this branch, the type of either seems to be: Data
    console.log('success', either)
}

Answer №1

Can you explain why the value of 'either' in the 'isFailure' branch is indicated as the intersection type of Data & CustomError rather than just CustomError?

The reason for this is that, after 'isFailure' check, it immediately follows 'either' initialization with a value of type 'Data'. The compiler utilizes type inference and control flow analysis to determine the types involved. It remembers that the 'either' value has a 'Data' type in this case. This can be confirmed if you try assigning 'either' to something declared as 'Data', as there will be no error:

const either2: Data = either; //no error, 'either' is of type 'Data' in this context

When the type of 'either' is not narrowed down, it functions as intended. Control flow analysis is limited to function boundaries, so within a function, the code will infer 'CustsomFailure' only:

function test(either: Either<Data, CustomFailure>) {
  if (isFailure<Data, CustomFailure>(either)) {
      // here 'either' is of type 'CustomFailure'
      console.log('failure', either);
  } else {
      // inside this branch, the type of 'either' is shown to be 'Data'
      console.log('success', either)
  }
}

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

What is the best way to create a routerlink that is both single-clickable and double-clickable within an

There have been numerous instances where a similar question has been raised, but I am unable to make this work. Despite looking at various answers such as this one on Stack Overflow that don't seem to work for most users. While this may not be specifi ...

Incorporating Imported Modules into the Final Build of a Typescript Project

In my Visual Studio Code Typescript project, I have set up some basic configurations and used npm to download libraries. One of the main files in my project is main.ts which includes the following code: import ApexCharts from 'apexcharts' var c ...

Splitting Angular modules into separate projects with identical configurations

My Angular project currently consists of approximately 20 different modules. Whenever there is a code change in one module, the entire project needs to be deployed. I am considering breaking down my modules into separate projects for individual deployment. ...

The 'items' property cannot be linked to 'virtual-scroller' as it is not a recognized attribute

I'm currently facing an issue with integrating virtual scroll into my Ionic 4 + Angular project. Previously, I relied on Ionic's implementation of virtual scroll (ion-virtual-scroll) which worked well initially. However, I encountered a major dr ...

Monitor constantly to determine if an element is within the visible portion of the screen

For a thorough understanding of my query, I feel the need to delve deeper. While I am well-versed in solving this issue with vanilla Javascript that is compatible with typescript, my struggle lies in figuring out how to invoke this function throughout th ...

Can the return type of a function be determined dynamically at runtime by analyzing a function parameter value?

Let's delve into this concept with an illustrative example! interface Query { foo: any; } interface Mutation { bar: any; } type OperationName = keyof Query | keyof Mutation; const request = async <T>(args, operationName: OperationName): P ...

remove a specific element from an array

Hey there! I'm attempting to remove only the keys from an array. Here's the initial array: {everyone: "everyone", random: "random", fast response time: "fast response time", less conversations: "less conversatio ...

What is the reason that {a: never} is not the same as never?

Is there a reason the code {a: never} cannot be simplified to just never? I believe this change would resolve the issues mentioned below. type A = {tag: 'A', value: number} type B = {tag: 'B', value: boolean} type N = {tag: never, valu ...

Generate a TypeScript generic function that maps class member types to class types

I am dealing with the following function in my codebase export async function batchEntitiesBy<Entity, T extends keyof Entity>( entityClass: EntityTarget<Entity> by: T, variables: readonly Entity[T][] ) { by: T, variables: readonly E ...

Changing the time in Angular while converting a string to a date object has proven to be

Can anyone help me with a problem I'm having trying to convert a String into a Date object without it being affected by timezone changes? Specifically, when I receive 2020-07-14T15:27:39Z from an http get request, I need to convert this into a Date o ...

What is the best way to implement filter functionality for individual columns in an Angular material table using ngFor?

I am using ngFor to populate my column names and corresponding data in Angular. How can I implement a separate filter row for each column in an Angular Material table? This filter row should appear below the header row, which displays the different column ...

Issues with TypeScript arise when transferring arguments between functions

Encountering a TypeScript error due to this pattern: Error message: 'Argument of type '(string | number)[]' is not assignable to parameter of type 'string[] | number[]' function foo(value: string | number) { return bar([va ...

Filtering an array in Angular based on two specific property values

I am facing a challenge with deleting items from an array based on two property values. If we were to compare it to the classic Sql delete command, the equivalent would look something like this: DELETE oImages WHERE idOffertRow = 1 and idProductImage = 2 ...

Disabling the intellisense feature for locale suggestions in Monaco is recommended

Switch the keyboard language to a different one (in this case Japanese using alt + shift), and when typing in Monaco editor, an intellisense menu appears with options to remove and search. Monaco Editor Version: V0.33.0 https://i.stack.imgur.com/SIyeV.pn ...

Using TypeScript to define values with the placeholder "%s" while inputting an object as a parameter

One common way to decorate strings is by using placeholders: let name = "Bob"; console.log("Hello, %s.", name) // => Outputs: "Hello, Bob." I'm curious if there's a way to access specific values within an object being passed in without specif ...

Discriminator-based deserializer with strong typing

Seeking advice on how to effectively utilize TypeScript for strongly typing a function that operates similarly to the following example: function createDeserializer(typeDeserializers) { return (data) => { const deserializer = typeDeserializ ...

Encountering Angular Material UI issues following the transition from version 11 to 12

I am currently in the process of updating my Angular application from version 11 to 12, integrating Angular Material, and encountering some error messages: Error No.1 - Error NG8002: Cannot bind to 'ngModel' as it is not a recognized property of ...

Theme not being rendered properly following the generation of a dynamic component in Angular

I am currently working on an Angular 9 application and I have successfully implemented a print functionality by creating components dynamically. However, I have encountered an issue where the CSS properties defined in the print-report.component.scss file a ...

Why is NestJs having trouble resolving dependencies?

Recently delving into NestJs, I followed the configuration instructions outlined in https://docs.nestjs.com/techniques/database, but I am struggling to identify the issue within my code. Error: Nest cannot resolve dependencies of the AdminRepository ...

Tips for implementing <mat-progress-bar> in .ts file when making API service requests with Angular

I'm currently utilizing an API call to retrieve an image from a service, and I would like to display a progress bar while the image is being fetched. It seems that I need to incorporate the progress bar within the service as the image data is returned ...