mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-18 07:29:16 -05:00
Merge pull request #10396 from Microsoft/fixNestedLoopTypeGuards
Fix nested loop type guards
This commit is contained in:
@@ -8366,13 +8366,18 @@ namespace ts {
|
||||
// each antecedent code path.
|
||||
const antecedentTypes: Type[] = [];
|
||||
let subtypeReduction = false;
|
||||
let firstAntecedentType: FlowType;
|
||||
flowLoopNodes[flowLoopCount] = flow;
|
||||
flowLoopKeys[flowLoopCount] = key;
|
||||
flowLoopTypes[flowLoopCount] = antecedentTypes;
|
||||
for (const antecedent of flow.antecedents) {
|
||||
flowLoopCount++;
|
||||
const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent));
|
||||
const flowType = getTypeAtFlowNode(antecedent);
|
||||
flowLoopCount--;
|
||||
if (!firstAntecedentType) {
|
||||
firstAntecedentType = flowType;
|
||||
}
|
||||
const type = getTypeFromFlowType(flowType);
|
||||
// If we see a value appear in the cache it is a sign that control flow analysis
|
||||
// was restarted and completed by checkExpressionCached. We can simply pick up
|
||||
// the resulting type and bail out.
|
||||
@@ -8395,7 +8400,13 @@ namespace ts {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cache[key] = getUnionType(antecedentTypes, subtypeReduction);
|
||||
// The result is incomplete if the first antecedent (the non-looping control flow path)
|
||||
// is incomplete.
|
||||
const result = getUnionType(antecedentTypes, subtypeReduction);
|
||||
if (isIncomplete(firstAntecedentType)) {
|
||||
return createFlowType(result, /*incomplete*/ true);
|
||||
}
|
||||
return cache[key] = result;
|
||||
}
|
||||
|
||||
function isMatchingReferenceDiscriminant(expr: Expression) {
|
||||
|
||||
63
tests/baselines/reference/nestedLoopTypeGuards.js
Normal file
63
tests/baselines/reference/nestedLoopTypeGuards.js
Normal file
@@ -0,0 +1,63 @@
|
||||
//// [nestedLoopTypeGuards.ts]
|
||||
// Repros from #10378
|
||||
|
||||
function f1() {
|
||||
var a: boolean | number | string;
|
||||
if (typeof a !== 'boolean') {
|
||||
// a is narrowed to "number | string"
|
||||
for (var i = 0; i < 1; i++) {
|
||||
for (var j = 0; j < 1; j++) {}
|
||||
if (typeof a === 'string') {
|
||||
// a is narrowed to "string'
|
||||
for (var j = 0; j < 1; j++) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
var a: string | number;
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
while (1) {}
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// [nestedLoopTypeGuards.js]
|
||||
// Repros from #10378
|
||||
function f1() {
|
||||
var a;
|
||||
if (typeof a !== 'boolean') {
|
||||
// a is narrowed to "number | string"
|
||||
for (var i = 0; i < 1; i++) {
|
||||
for (var j = 0; j < 1; j++) { }
|
||||
if (typeof a === 'string') {
|
||||
// a is narrowed to "string'
|
||||
for (var j = 0; j < 1; j++) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function f2() {
|
||||
var a;
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
while (1) { }
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
tests/baselines/reference/nestedLoopTypeGuards.symbols
Normal file
66
tests/baselines/reference/nestedLoopTypeGuards.symbols
Normal file
@@ -0,0 +1,66 @@
|
||||
=== tests/cases/compiler/nestedLoopTypeGuards.ts ===
|
||||
// Repros from #10378
|
||||
|
||||
function f1() {
|
||||
>f1 : Symbol(f1, Decl(nestedLoopTypeGuards.ts, 0, 0))
|
||||
|
||||
var a: boolean | number | string;
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7))
|
||||
|
||||
if (typeof a !== 'boolean') {
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7))
|
||||
|
||||
// a is narrowed to "number | string"
|
||||
for (var i = 0; i < 1; i++) {
|
||||
>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16))
|
||||
>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16))
|
||||
>i : Symbol(i, Decl(nestedLoopTypeGuards.ts, 6, 16))
|
||||
|
||||
for (var j = 0; j < 1; j++) {}
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
|
||||
if (typeof a === 'string') {
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7))
|
||||
|
||||
// a is narrowed to "string'
|
||||
for (var j = 0; j < 1; j++) {
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
>j : Symbol(j, Decl(nestedLoopTypeGuards.ts, 7, 20), Decl(nestedLoopTypeGuards.ts, 10, 24))
|
||||
|
||||
a.length; // Should not error here
|
||||
>a.length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 3, 7))
|
||||
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
>f2 : Symbol(f2, Decl(nestedLoopTypeGuards.ts, 16, 1))
|
||||
|
||||
var a: string | number;
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7))
|
||||
|
||||
if (typeof a === 'string') {
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7))
|
||||
|
||||
while (1) {
|
||||
while (1) {}
|
||||
if (typeof a === 'string') {
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7))
|
||||
|
||||
while (1) {
|
||||
a.length; // Should not error here
|
||||
>a.length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
>a : Symbol(a, Decl(nestedLoopTypeGuards.ts, 19, 7))
|
||||
>length : Symbol(String.length, Decl(lib.d.ts, --, --))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
tests/baselines/reference/nestedLoopTypeGuards.types
Normal file
96
tests/baselines/reference/nestedLoopTypeGuards.types
Normal file
@@ -0,0 +1,96 @@
|
||||
=== tests/cases/compiler/nestedLoopTypeGuards.ts ===
|
||||
// Repros from #10378
|
||||
|
||||
function f1() {
|
||||
>f1 : () => void
|
||||
|
||||
var a: boolean | number | string;
|
||||
>a : string | number | boolean
|
||||
|
||||
if (typeof a !== 'boolean') {
|
||||
>typeof a !== 'boolean' : boolean
|
||||
>typeof a : string
|
||||
>a : string | number | boolean
|
||||
>'boolean' : "boolean"
|
||||
|
||||
// a is narrowed to "number | string"
|
||||
for (var i = 0; i < 1; i++) {
|
||||
>i : number
|
||||
>0 : number
|
||||
>i < 1 : boolean
|
||||
>i : number
|
||||
>1 : number
|
||||
>i++ : number
|
||||
>i : number
|
||||
|
||||
for (var j = 0; j < 1; j++) {}
|
||||
>j : number
|
||||
>0 : number
|
||||
>j < 1 : boolean
|
||||
>j : number
|
||||
>1 : number
|
||||
>j++ : number
|
||||
>j : number
|
||||
|
||||
if (typeof a === 'string') {
|
||||
>typeof a === 'string' : boolean
|
||||
>typeof a : string
|
||||
>a : string | number
|
||||
>'string' : "string"
|
||||
|
||||
// a is narrowed to "string'
|
||||
for (var j = 0; j < 1; j++) {
|
||||
>j : number
|
||||
>0 : number
|
||||
>j < 1 : boolean
|
||||
>j : number
|
||||
>1 : number
|
||||
>j++ : number
|
||||
>j : number
|
||||
|
||||
a.length; // Should not error here
|
||||
>a.length : number
|
||||
>a : string
|
||||
>length : number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
>f2 : () => void
|
||||
|
||||
var a: string | number;
|
||||
>a : string | number
|
||||
|
||||
if (typeof a === 'string') {
|
||||
>typeof a === 'string' : boolean
|
||||
>typeof a : string
|
||||
>a : string | number
|
||||
>'string' : "string"
|
||||
|
||||
while (1) {
|
||||
>1 : number
|
||||
|
||||
while (1) {}
|
||||
>1 : number
|
||||
|
||||
if (typeof a === 'string') {
|
||||
>typeof a === 'string' : boolean
|
||||
>typeof a : string
|
||||
>a : string
|
||||
>'string' : "string"
|
||||
|
||||
while (1) {
|
||||
>1 : number
|
||||
|
||||
a.length; // Should not error here
|
||||
>a.length : number
|
||||
>a : string
|
||||
>length : number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
tests/cases/compiler/nestedLoopTypeGuards.ts
Normal file
31
tests/cases/compiler/nestedLoopTypeGuards.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// Repros from #10378
|
||||
|
||||
function f1() {
|
||||
var a: boolean | number | string;
|
||||
if (typeof a !== 'boolean') {
|
||||
// a is narrowed to "number | string"
|
||||
for (var i = 0; i < 1; i++) {
|
||||
for (var j = 0; j < 1; j++) {}
|
||||
if (typeof a === 'string') {
|
||||
// a is narrowed to "string'
|
||||
for (var j = 0; j < 1; j++) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
var a: string | number;
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
while (1) {}
|
||||
if (typeof a === 'string') {
|
||||
while (1) {
|
||||
a.length; // Should not error here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user