Skip to content

allow storing results of narrowing of class properties in booleans for further narrowing #53267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
SteveRusin opened this issue Mar 15, 2023 · 3 comments · Fixed by #54347
Closed
Labels
Bug A bug in TypeScript Help Wanted You can do this
Milestone

Comments

@SteveRusin
Copy link

Bug Report

🔎 Search Terms

https://www.google.com/search?q=typescript+does+not+narrow+type+of+stored+variable&oq=typescript+does+not+narrow+type+of+stored+variable&aqs=chrome..69i57j69i64.8615j0j7&sourceid=chrome&ie=UTF-8

🕗 Version & Regression Information

  • This is the behavior in every version I tried

⏯ Playground Link

Playground link with relevant code

💻 Code

class Utils {
  static isDefined<T>(value: T): value is NonNullable<T> {
    return value != null;
  };
}

class A {
  public testNumber: number | undefined;

  constructor() {
    const isNumber = Utils.isDefined(this.testNumber);

    if (isNumber) {
      // Type 'number | undefined' is not assignable to type 'number'
      const x: number = this.testNumber;
    }
  }
}

function foo(x: number | undefined) {
  const isNum = Utils.isDefined(x);

  if(isNum) {
    // Works. No error here
    const y: number = x;
  }
}

🙁 Actual behavior

The narrowed type of class property is not inferred if narrowing result stored in variable

I expect the type to be correctly inferred as it's working for regular variables inside a function
This PR fixed this bug for functions

@fatcerberus
Copy link

fatcerberus commented Mar 15, 2023

This seems intentional and is not specific to classes:

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.

testNumber is not readonly.

@MartinJohns
Copy link
Contributor

@fatcerberus But even if you make it readonly it's not working.

@RyanCavanaugh RyanCavanaugh added Bug A bug in TypeScript Help Wanted You can do this labels Mar 17, 2023
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Mar 17, 2023
@RyanCavanaugh
Copy link
Member

It does need to be readonly. Looks like we have a bug here when the property is on this specifically, since this code works as expected:

class Utils {
  static isDefined<T>(value: T): value is NonNullable<T> {
    return value != null;
  };
}

class A {
  public readonly testNumber: number | undefined;

  foo() {
    const obj: this = this;
    const isNumber = Utils.isDefined(obj.testNumber);

    if (isNumber) {
      // ok
      const x: number = obj.testNumber;
    }
  }
}

Common source of bug here when we check for something being an Identifier, which this isn't. From a type perspective these examples are the same so it's probably a simple fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Help Wanted You can do this
Projects
None yet
4 participants