diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf468622ea6..3a108888253 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20097,7 +20097,7 @@ namespace ts { const symbol = getResolvedSymbol(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((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); } diff --git a/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.errors.txt b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.errors.txt new file mode 100644 index 00000000000..85f445fdde4 --- /dev/null +++ b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.errors.txt @@ -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'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.js b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.js new file mode 100644 index 00000000000..68ad3c5dd7c --- /dev/null +++ b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.js @@ -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; +}()); diff --git a/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.symbols b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.symbols new file mode 100644 index 00000000000..c36f9a19445 --- /dev/null +++ b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.symbols @@ -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)) + } +} diff --git a/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.types b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.types new file mode 100644 index 00000000000..7938c5ec6e1 --- /dev/null +++ b/tests/baselines/reference/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.types @@ -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 + } +} diff --git a/tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts b/tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts new file mode 100644 index 00000000000..0270eee4fe6 --- /dev/null +++ b/tests/cases/conformance/classes/members/instanceAndStaticMembers/typeOfThisInstanceMemberNarrowedWithLoopAntecedent.ts @@ -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 + } +} \ No newline at end of file