Home > Blockchain >  Why would "infer" in conditional type change the return type?
Why would "infer" in conditional type change the return type?

Time:12-08

I was playing with conditional types and stubbled upon this problem :

import { FormGroup, FormControl, AbstractControl } from '@angular/forms';
type Foo = FormGroup<{ title: FormControl<string> }>
type Bar = FormGroup<{ title2: FormControl<string>; }>;

type Baz = Foo | Bar extends FormGroup<infer U> ? 'ok ' : 'nok'; // NOK
//   ^?
type Baz2 = Foo | Bar extends FormGroup ? 'ok ' : 'nok'; // OK
//   ^?

Why would the presence of infer change the return type here ?

Playground

CodePudding user response:

The compiler should normally be able to infer U. I am not sure yet what the underlying issue is but I tracked it down to the following:

The inference fails because FormGroup contains methods which have a conditional in their return type.

Without the conditional, inference works fine.

type Foo = FormGroup<{ title: string }>
type Bar = FormGroup<{ title2: string }>;

export declare class FormGroup<TControl = any> {    
    fn1(arg: TControl): TControl
//  fn2(arg: TControl): 0 extends TControl ? true : false
}

type Baz = Foo | Bar extends FormGroup<infer U> ? true : false;
//   ^? type Baz = true

As soon as we introduce the conditional in a return type, the inference breaks.

type Foo = FormGroup<{ title: string }>
type Bar = FormGroup<{ title2: string }>;

export declare class FormGroup<TControl = any> {    
    fn1(arg: TControl): TControl
    fn2(arg: TControl): 0 extends TControl ? true : false
}

type Baz = Foo | Bar extends FormGroup<infer U> ? true : false;
//   ^? type Baz = false

I may do some further investigation into this issue.

possibly related:


Playground

CodePudding user response:

The infer keyword is used to narrow the type of an expression.

In the first case, the type of the expression Foo | Bar is the union of the types Foo and Bar.

The first type is FormGroup<{ title: FormControl<string> }> while the second one is FormGroup<{ title2: FormControl<string> }>.

Since these types aren't the same, their union is not equal to FormGroup<infer U>.

But this is not true in the second case, because both Foo and Bar are a subtype of FormGroup, for this the second case results in ok

  • Related