Skip to content

5.6 regression: Incorrect param type inference for type with all optional props #59656

Description

🔎 Search Terms

inference, param

🕗 Version & Regression Information

  • This changed between versions 5.5.4 and 5.6.0

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.0-dev.20240816#code/JYOwLgpgTgZghgYwgAgMoHsC2EAqBPABxQG8AoZC5AeiuQHkRkBWAOgDYWAGAGmQgA8iUYNnDIARnmSY4Aa1ABzZGAAWwAM7ICUdAWRQIARwCuwAwBMW5SsZDATEAAo6CAfgBcydWGEgFAblIAX1JScwgEABs4A2QEdBBvZHRxdU86VOgANzhxSIgAHgxsfCIAPkDSGFsEMGAE5BiFAHEIMHUAdR0-UogACgBKdMyoHLzC4iCy5DJKfTbjKEYU9RYCYCI+sDgCPqbkAF5p2bnKJsDTyhoAPVdrS5pkAFFBCMhzT2LcQhRQZhZWAAWe6nR4AQVqxjgkU8FEmyD+WVYHE4AFpxG04Mp0P8UajwlkWAAmThEwGcAAcAEY2Mg9mBkPk4N4BiCggMBoEQlUanUGk1Wu0AMLoKAGWq9QYzEEGMCLZapNYbfrbXb7I7Sy4Uc4guY3O5a6i0MGRADucDwmniYrebI5XNCoEgsEQKAy6myuXyBRwxxB6yIBTBZT6unSQjgYFFADFefUQD7eMGhvQRmNvcGHU7oPAkMgAKogGJ4WMgWrxxPIABKxzp6nQiyQnhwKar-mQIWzLrzdAjUagpfLCUrNb4-EgIHMmkLxcHfIT7s94x9ZV4i9GXsKNdrnfAOddyAAsgl0L1e9BIzG48PfWOJ1P6H2r2X55Xb8QO2EItFYtUX-HlB2Fc+hAAQwE8PockiYwIGbAZDmmLJ0GAcwU2PEBTx+c8oEvAdrwTX1AiAA

💻 Code

interface SomeType {
    // On 5.6.0, experiment by making this prop required.
    uniqueProp?: string;
}

declare const obs: Observable<SomeType>;

function argGetsWrongType(): Observable<{}> {
    return obs.pipe(tap(arg => {
        arg;
        //^?
        // Expected: SomeType in 5.5.4
        // Actual:   {} in v5.6.0-beta to 5.6.0-dev.20240816 (at least)
    }));
}

function argGetsCorrectType() {
    return obs.pipe(tap(arg => {
        arg;
        //^?
        // Always correct
    }));
}

interface Observable<T> {
    pipe<A>(op: OperatorFunction<T, A>): Observable<A>;
}
interface UnaryFunction<T, R> { (source: T): R; }
interface OperatorFunction<T, R> extends UnaryFunction<Observable<T>, Observable<R>> { }
interface MonoTypeOperatorFunction<T> extends OperatorFunction<T, T> { }
declare function tap<T>(next: (value: T) => void): MonoTypeOperatorFunction<T>;

🙁 Actual behavior

arg is inferred as {}

🙂 Expected behavior

arg is inferred as SomeType as was consistent in v5.5.4

Additional information about the issue

(Google note: See http://cl/664889390 for local workaround)

Activity

  1. Andarist commented on Aug 16, 2024

    @Andarist
    Contributor

    Bisects to this diff, so the change likely was introduced by #57909

  2. trevorade commented on Aug 16, 2024

    @trevorade
    ContributorAuthor

    FWIW, in Google's codebase, I'm seeing other new inference related bugs popping up. For the life of me, I can't make a simplified repro for one particular case that I've been looking at for a while...

    I'll try to add other repros when I can...

  3. Andarist commented on Aug 16, 2024

    @Andarist
    Contributor

    I’d appreciate any kind of repros - they could even be not-so-minimal 😉

  4. RyanCavanaugh commented on Aug 16, 2024

    @RyanCavanaugh
    Member

    A useful observation is that this can be fixed by a single variance annotation

    interface Observable<in T> {

    which implies we're likely measuring the variance wrong.

  5. trevorade commented on Aug 16, 2024

    @trevorade
    ContributorAuthor

    I’d appreciate any kind of repros - they could even be not-so-minimal 😉

    Yeah. This one is just some internal team who has produced a pipeline with some pretty complicated TS types and inference is breaking in a new way. I'll be looking more at overall compilation issues in the codebase and will try to highlight other similar issues when they seem relevant.

  6. Andarist commented on Aug 20, 2024

    @Andarist
    Contributor

    This changes the behavior even in 5.5:

    interface Observable<T> {
      pipe<R>(op: OperatorFunction<T, R>): Observable<R>;
    }
    
    type OperatorFunction<T, R> = (source: Observable<T>) => Observable<R>;
    
    -declare function tap<T>(next: (value: T) => void): OperatorFunction<T, T>;
    
    +interface UnaryFunction<T, R> { (source: T): R; }
    +declare function tap<T>(next: (value: T) => void): UnaryFunction<Observable<T>, Observable<T>>
    
    declare const obs: Observable<{ a?: string; b?: number }>;
    
    function test(): Observable<{ a?: string }> {
      return obs.pipe(
        tap((tapped) => {
          tapped;
          // ^?
        }),
      );
    }

    And, like Ryan suspected, T in Observable<T> is measured to have VarianceFlags.Independent here. That means it was first measured to have VarianceFlags.Bivariant (TS playground):

    interface Observable<T> {
      pipe<R>(op: OperatorFunction<T, R>): Observable<R>;
    }
    
    type OperatorFunction<T, R> = (source: Observable<T>) => Observable<R>;
    
    type Test1 = Observable<string | number> extends Observable<string> ? 1 : 0;
    //   ^? type Test1 = 1
    type Test2 = Observable<string> extends Observable<string | number> ? 1 : 0;
    //   ^? type Test2 = 1

    Declaring pipe as a property instead of a method doesn't change this (TS playground).

    Clearly, we get different results here when either type arguments-based or structure-based inference is used. For now, I'll look into understanding what changed for the latter with my PR.

  7. trevorade commented on Aug 22, 2024

    @trevorade
    ContributorAuthor

    By the way, I've identified a number of other new compilation issues a little different from this that I believe is related to this issue.

    Once there is a new 5.6.0 build with this PR, I can see if it resolves the other new issues I've been seeing.

    In this example, basically, inference appears to be dropping optional parameters from a type or making them required. I haven't dug into it super deep. If this PR doesn't fix the issue, I'll try to boil it down to a repro.

  8. jakebailey commented on Aug 22, 2024

    @jakebailey
    Member

    Can you try the build on #59709 (comment) to see if that fixes your issues?

  9. trevorade commented on Aug 26, 2024

    @trevorade
    ContributorAuthor

    Hey there. Confirmed that this fix does fix the specific issue noted here.

    That said, I am still seeing other new inference-related bugs that I was hoping this fix would resolve. I'll try to construct repros in TS Playground for those ones and file new issues.

  10. changed the title [-]5.6.0 regression: Incorrect param type inference for type with all optional props[/-] [+]5.6 regression: Incorrect param type inference for type with all optional props[/+] on Aug 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: check: Type InferenceRelated to type inference performed during signature resolution or `infer` type resolutionHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions