-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type is not referred correctly in the while loop #59715
Comments
With removed redundant pieces (TS playground): declare class Child {
parent: Parent | null;
}
declare class Parent {
parent: GrandParent | null;
}
declare class GrandParent {
parent: GreatGrandParent | null;
}
declare class GreatGrandParent {
parent: null;
}
declare const child: Child;
let currentParent: GreatGrandParent | GrandParent | Parent | null = child.parent;
// // the bug doesn't happen with this:
// declare let currentParent: GreatGrandParent | GrandParent | Parent | null;
while (currentParent) {
// actual: GrandParent | Parent
// expected: GreatGrandParent | GrandParent | Parent
currentParent;
currentParent = currentParent.parent;
currentParent; // Parent | GrandParent | GreatGrandParent | null
} |
Isn't this just because |
Same thing happens even if we make all of those classes nominal: TS playground |
This is 100% a bug but one that I don't know how to fix. I hope the above will help somebody to do it π |
@RyanCavanaugh shouldn't this really be classified as a bug if the CFA incorrectly drops one of the possible types? It's not that this isn't precise enough - it's plain incorrect. Take a look at this extension of the problem: declare class Child {
parent: Parent | null;
}
declare class Parent {
parent: GrandParent | null;
prop: number
}
declare class GrandParent {
parent: GreatGrandParent | null;
prop: number
}
declare class GreatGrandParent {
parent: null;
}
declare const child: Child;
let currentParent: GreatGrandParent | GrandParent | Parent | null = child.parent;
while (currentParent) {
// since `currentParent` doesn't include `GreatGrandParent` (even though it should),
// this is allowed but it shouldn't!
const num: number = currentParent.prop
currentParent = currentParent.parent;
} |
Last known good: 4.3.0-dev.20210319 Related #43183 |
Some progress note. I can confirm the change in #43183 was the root cause.
Consider: while (currentParent) {
// actual: GrandParent | Parent
// expected: GreatGrandParent | GrandParent | Parent
currentParent; <<<<< this line
currentParent = currentParent.parent;
currentParent; // Parent | GrandParent | GreatGrandParent | null
} Call stack: checkExpression()
checkExpressionWorker()
checkIdentifier()
getFlowTypeOfReference()
getTypeAtFlowNode()
getTypeAtFlowLoopLabel() // in a loop, proceeds to the second antecedent
getTypeAtFlowNode()
getTypeAtFlowAssignment()
getInitialOrAssignedType() // It actually goes on to the initialType branch. And interestingly, if we patch it to always return assignedType, it works as expected
getInitialType()
getInitialTypeOfVariableDeclaration()
getTypeOfInitializer()
getTypeOfExpression()
checkExpression(..., CheckMode.TypeOnly)
checkExpressionWorker(..., CheckMode.TypeOnly)
checkIdentifier(..., CheckMode.TypeOnly)
getNarrowedTypeOfSymbol()
... I wonder where should we apply the fix. And what might be the edge cases. |
The behavior before #43183 isn't significantly better, as it still fails if you add one more layer: type All = A1 | A2 | A3 | A4;
class A1 {
nom = "a1" as const
next() {
return new A2;
}
}
class A2 {
nom = "a2" as const
next() {
return new A3;
}
}
class A3 {
nom = "a3" as const
next() {
return new A4;
}
}
class A4 {
nom = "a4" as const
next() {
return null;
}
}
function foo(a: A1) {
for (let p: All | null = a; p != null; p = p.next()) {
// In 4.2.3, `p` is A1 | A2 | A3
// In 5.7.2, `p` is A1 | A2
p;
if (p.nom === "a4") {
// `p` is never in all versions
p;
console.log("This is hit at runtime");
}
}
}
foo(new A1); |
π Search Terms
incorrect type while loop
π Version & Regression Information
Worked correctly in version 4.2.3.
It doesn't work since 4.3.5.
β― Playground Link
https://www.typescriptlang.org/play/?ts=5.5.4#code/MYGwhgzhAEDCAWBLEATaBTAHgF3QOxRgAkAVAWQBkBREdAW322gG8AoASAHN0mAHMAE6MAFAEoWHdsAD2eCE2bR+QvNhr1G0AL7QAvNGxIIAblaTEAM2jCAhMOWN1DVdERzsYPMHTSrABUFGUXE2djChbABXATxoPEiQEFMwrTNwnmjYh1UnRmTU1NZQSBgAlSYsXAJicmpaZwUObj5A1TEJMJl3FiVWtXrNHX1DRBM09ktrO2z+jRc3eU9vX2gAcQFPFDKgkMl2CMy4hKTJQvSomN7y3NV81kLiqDWNgm2XSvxCaFJKG8auHhXES7TqyeQ9GZ-bR6AxGUzmKy2ex9KELDxeHxWdboMDYdabN7YYIdc6HeKJZLsM77DKXSEDW4cApmR4wbG4-GvPoYHCfGq-Bn-ZpAtogmkXWLkk5U+4syLyaR0P4QAB0KHQFjc6GEACJgABaYBIVA6gA0cGNKFEpmA8uwiuVao1Wt1BpmZughOtRTtDsFqvVmrw2r1+s4LxQ+vd5s5Wz63ttCqV-qdQZDBvDOOwYYjUb6HvZeIjXvh2AAnrx0J6Uf6YYXY4ToAAfZ4E7ktwnwrrgo3IND6YMAdwtfbE8NoTFtAnKhIAXNXrrWW1KYb3UCqZvDB8aq8IpzP4x1u9JaCqQNJOHvogfytboAB6e-QCDwaQJNAAIyr9eL7dbXPKZsF0YNJ90YRt9DA1RCQ3PpTFSIA
π» Code
π Actual behavior
The
curerntParent
's type is not referred correctly. It becameParent | GrandParent
.π Expected behavior
The
currentParent
's type should beParent | GrandParent | GreatGrandParent
.Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: