Fix 31995: make cached key more precise to avoid returning wrong cached value. (#39670)

* fix 31995

* revert useless change only for debug.

* add test
This commit is contained in:
Song
2020-07-23 00:26:17 +08:00
committed by GitHub
parent 294a0406e3
commit 8a05707559
6 changed files with 329 additions and 4 deletions

View File

@@ -20097,7 +20097,7 @@ namespace ts {
const symbol = getResolvedSymbol(<Identifier>node);
return symbol !== unknownSymbol ? `${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}|${isConstraintPosition(node) ? "@" : ""}${getSymbolId(symbol)}` : undefined;
case SyntaxKind.ThisKeyword:
return "0";
return `0|${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}`;
case SyntaxKind.NonNullExpression:
case SyntaxKind.ParenthesizedExpression:
return getFlowCacheKey((<NonNullExpression | ParenthesizedExpression>node).expression, declaredType, initialType, flowContainer);
@@ -20960,7 +20960,7 @@ namespace ts {
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
let key: string | undefined;
let keySet = false;
let isKeySet = false;
let flowDepth = 0;
if (flowAnalysisDisabled) {
return errorType;
@@ -20983,10 +20983,10 @@ namespace ts {
return resultType;
function getOrSetCacheKey() {
if (keySet) {
if (isKeySet) {
return key;
}
keySet = true;
isKeySet = true;
return key = getFlowCacheKey(reference, declaredType, initialType, flowContainer);
}

View File

@@ -0,0 +1,39 @@
tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts(29,13): error TS2322: Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
==== tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts (1 errors) ====
// #31995
type State = {
type: "numberVariant";
data: number;
} | {
type: "stringVariant";
data: string;
};
class SomeClass {
state!: State;
method() {
while (0) { }
this.state.data;
if (this.state.type === "stringVariant") {
const s: string = this.state.data;
}
}
}
class SomeClass2 {
state!: State;
method() {
const c = false;
while (c) { }
if (this.state.type === "numberVariant") {
this.state.data;
}
let n: number = this.state?.data; // This should be an error
~
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
}
}

View File

@@ -0,0 +1,60 @@
//// [typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts]
// #31995
type State = {
type: "numberVariant";
data: number;
} | {
type: "stringVariant";
data: string;
};
class SomeClass {
state!: State;
method() {
while (0) { }
this.state.data;
if (this.state.type === "stringVariant") {
const s: string = this.state.data;
}
}
}
class SomeClass2 {
state!: State;
method() {
const c = false;
while (c) { }
if (this.state.type === "numberVariant") {
this.state.data;
}
let n: number = this.state?.data; // This should be an error
}
}
//// [typeOfThisInstanceMemberNarrowedWithLoopAntecedent.js]
var SomeClass = /** @class */ (function () {
function SomeClass() {
}
SomeClass.prototype.method = function () {
while (0) { }
this.state.data;
if (this.state.type === "stringVariant") {
var s = this.state.data;
}
};
return SomeClass;
}());
var SomeClass2 = /** @class */ (function () {
function SomeClass2() {
}
SomeClass2.prototype.method = function () {
var _a;
var c = false;
while (c) { }
if (this.state.type === "numberVariant") {
this.state.data;
}
var n = (_a = this.state) === null || _a === void 0 ? void 0 : _a.data; // This should be an error
};
return SomeClass2;
}());

View File

@@ -0,0 +1,95 @@
=== tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts ===
// #31995
type State = {
>State : Symbol(State, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 0, 0))
type: "numberVariant";
>type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 1, 14))
data: number;
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26))
} | {
type: "stringVariant";
>type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 4, 5))
data: string;
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
};
class SomeClass {
>SomeClass : Symbol(SomeClass, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 7, 2))
state!: State;
>state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>State : Symbol(State, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 0, 0))
method() {
>method : Symbol(SomeClass.method, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 10, 18))
while (0) { }
this.state.data;
>this.state.data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
>this.state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>this : Symbol(SomeClass, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 7, 2))
>state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
if (this.state.type === "stringVariant") {
>this.state.type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 1, 14), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 4, 5))
>this.state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>this : Symbol(SomeClass, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 7, 2))
>state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 1, 14), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 4, 5))
const s: string = this.state.data;
>s : Symbol(s, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 15, 17))
>this.state.data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
>this.state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>this : Symbol(SomeClass, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 7, 2))
>state : Symbol(SomeClass.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 9, 17))
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
}
}
}
class SomeClass2 {
>SomeClass2 : Symbol(SomeClass2, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 18, 1))
state!: State;
>state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>State : Symbol(State, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 0, 0))
method() {
>method : Symbol(SomeClass2.method, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 21, 18))
const c = false;
>c : Symbol(c, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 23, 13))
while (c) { }
>c : Symbol(c, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 23, 13))
if (this.state.type === "numberVariant") {
>this.state.type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 1, 14), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 4, 5))
>this.state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>this : Symbol(SomeClass2, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 18, 1))
>state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>type : Symbol(type, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 1, 14), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 4, 5))
this.state.data;
>this.state.data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26))
>this.state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>this : Symbol(SomeClass2, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 18, 1))
>state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26))
}
let n: number = this.state?.data; // This should be an error
>n : Symbol(n, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 28, 11))
>this.state?.data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
>this.state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>this : Symbol(SomeClass2, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 18, 1))
>state : Symbol(SomeClass2.state, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 20, 18))
>data : Symbol(data, Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 2, 26), Decl(typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts, 5, 26))
}
}

View File

@@ -0,0 +1,100 @@
=== tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts ===
// #31995
type State = {
>State : State
type: "numberVariant";
>type : "numberVariant"
data: number;
>data : number
} | {
type: "stringVariant";
>type : "stringVariant"
data: string;
>data : string
};
class SomeClass {
>SomeClass : SomeClass
state!: State;
>state : State
method() {
>method : () => void
while (0) { }
>0 : 0
this.state.data;
>this.state.data : string | number
>this.state : State
>this : this
>state : State
>data : string | number
if (this.state.type === "stringVariant") {
>this.state.type === "stringVariant" : boolean
>this.state.type : "numberVariant" | "stringVariant"
>this.state : State
>this : this
>state : State
>type : "numberVariant" | "stringVariant"
>"stringVariant" : "stringVariant"
const s: string = this.state.data;
>s : string
>this.state.data : string
>this.state : { type: "stringVariant"; data: string; }
>this : this
>state : { type: "stringVariant"; data: string; }
>data : string
}
}
}
class SomeClass2 {
>SomeClass2 : SomeClass2
state!: State;
>state : State
method() {
>method : () => void
const c = false;
>c : false
>false : false
while (c) { }
>c : false
if (this.state.type === "numberVariant") {
>this.state.type === "numberVariant" : boolean
>this.state.type : "numberVariant" | "stringVariant"
>this.state : State
>this : this
>state : State
>type : "numberVariant" | "stringVariant"
>"numberVariant" : "numberVariant"
this.state.data;
>this.state.data : number
>this.state : { type: "numberVariant"; data: number; }
>this : this
>state : { type: "numberVariant"; data: number; }
>data : number
}
let n: number = this.state?.data; // This should be an error
>n : number
>this.state?.data : string | number
>this.state : State
>this : this
>state : State
>data : string | number
}
}

View File

@@ -0,0 +1,31 @@
// #31995
type State = {
type: "numberVariant";
data: number;
} | {
type: "stringVariant";
data: string;
};
class SomeClass {
state!: State;
method() {
while (0) { }
this.state.data;
if (this.state.type === "stringVariant") {
const s: string = this.state.data;
}
}
}
class SomeClass2 {
state!: State;
method() {
const c = false;
while (c) { }
if (this.state.type === "numberVariant") {
this.state.data;
}
let n: number = this.state?.data; // This should be an error
}
}