From b332e0f8889339d73de89194286afc6fad166a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 9 Dec 2024 08:16:32 +0100 Subject: [PATCH] Control flow analysis for element access with variable constant-like index in for statements --- src/compiler/checker.ts | 24 +- ...ntrolFlowComputedPropertyNames2.errors.txt | 51 ++++ .../controlFlowComputedPropertyNames2.symbols | 141 +++++++++ .../controlFlowComputedPropertyNames2.types | 285 ++++++++++++++++++ .../controlFlowComputedPropertyNames3.symbols | 28 ++ .../controlFlowComputedPropertyNames3.types | 66 ++++ .../controlFlowComputedPropertyNames2.ts | 41 +++ .../controlFlowComputedPropertyNames3.ts | 12 + 8 files changed, 646 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames2.errors.txt create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames2.symbols create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames2.types create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames3.symbols create mode 100644 tests/baselines/reference/controlFlowComputedPropertyNames3.types create mode 100644 tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b439b7fd9c579..6dd78840acf83 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27135,7 +27135,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isElementAccessExpression(node) && isIdentifier(node.argumentExpression)) { const symbol = getResolvedSymbol(node.argumentExpression); - if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol)) { + if (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol) || isBlockScopedForStatementVariable(symbol) && isUsedInForStatementBody(symbol, node.argumentExpression) && !isSymbolAssignedInForStatementBody(symbol)) { const key = getFlowCacheKey((node as AccessExpression).expression, declaredType, initialType, flowContainer); return key && `${key}.@${getSymbolId(symbol)}`; } @@ -27192,7 +27192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isElementAccessExpression(source) && isElementAccessExpression(target) && isIdentifier(source.argumentExpression) && isIdentifier(target.argumentExpression)) { const symbol = getResolvedSymbol(source.argumentExpression); - if (symbol === getResolvedSymbol(target.argumentExpression) && (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol))) { + if (symbol === getResolvedSymbol(target.argumentExpression) && (isConstantVariable(symbol) || isParameterOrMutableLocalVariable(symbol) && !isSymbolAssigned(symbol) || isBlockScopedForStatementVariable(symbol) && isUsedInForStatementBody(symbol, source.argumentExpression) && !isSymbolAssignedInForStatementBody(symbol))) { return isMatchingReference(source.expression, target.expression); } } @@ -29632,6 +29632,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isSomeSymbolAssignedWorker(rootDeclaration.name); } + function isSymbolAssignedInForStatementBody(symbol: Symbol) { + const forStatement = getRootDeclaration(symbol.valueDeclaration!).parent.parent; + Debug.assert(isForStatement(forStatement)); + return !isPastLastAssignment(symbol, forStatement.statement); + } + + function isUsedInForStatementBody(symbol: Symbol, location: Node) { + const forStatement = getRootDeclaration(symbol.valueDeclaration!).parent.parent; + Debug.assert(isForStatement(forStatement)); + return location.pos >= forStatement.statement.pos && location.end <= forStatement.statement.end; + } + function isSomeSymbolAssignedWorker(node: BindingName): boolean { if (node.kind === SyntaxKind.Identifier) { return isSymbolAssigned(getSymbolOfDeclaration(node.parent as Declaration)); @@ -29742,6 +29754,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ); } + function isBlockScopedForStatementVariable(symbol: Symbol) { + if (!(symbol.flags & SymbolFlags.BlockScopedVariable)) { + return false; + } + const declaration = symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration); + return !!declaration && isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForStatement; + } + function parameterInitializerContainsUndefined(declaration: ParameterDeclaration): boolean { const links = getNodeLinks(declaration); diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames2.errors.txt b/tests/baselines/reference/controlFlowComputedPropertyNames2.errors.txt new file mode 100644 index 0000000000000..d2c0df9752dfb --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames2.errors.txt @@ -0,0 +1,51 @@ +controlFlowComputedPropertyNames2.ts(20,49): error TS2339: Property 'type' does not exist on type 'Type'. +controlFlowComputedPropertyNames2.ts(28,39): error TS2339: Property 'type' does not exist on type 'Type'. +controlFlowComputedPropertyNames2.ts(35,39): error TS2339: Property 'type' does not exist on type 'Type'. + + +==== controlFlowComputedPropertyNames2.ts (3 errors) ==== + interface Type { + kind: number; + isIndexType(): this is IndexType; + } + + interface IndexType extends Type { + kind: 1; + type: Type; + } + + function test1(types: Type[]) { + for (let i = 0; i < types.length; i++) { + const t = types[i].isIndexType() ? types[i].type : types[i]; // ok + } + } + + function test2(types: Type[]) { + for (let i = 0; i < types.length; i++) { + i++; + const t = types[i].isIndexType() ? types[i].type : types[i]; // error + ~~~~ +!!! error TS2339: Property 'type' does not exist on type 'Type'. + } + } + + function test3(types: Type[]) { + for ( + let i = 0; + i < types.length; + types[i].isIndexType() ? types[i].type : types[i], i++ // error + ~~~~ +!!! error TS2339: Property 'type' does not exist on type 'Type'. + ) {} + } + + function test4(types: Type[]) { + for ( + let i = 0; + types[i].isIndexType() ? types[i].type : types[i], i < types.length; // error + ~~~~ +!!! error TS2339: Property 'type' does not exist on type 'Type'. + i++ + ) {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames2.symbols b/tests/baselines/reference/controlFlowComputedPropertyNames2.symbols new file mode 100644 index 0000000000000..7de4e785b457d --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames2.symbols @@ -0,0 +1,141 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts] //// + +=== controlFlowComputedPropertyNames2.ts === +interface Type { +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + kind: number; +>kind : Symbol(Type.kind, Decl(controlFlowComputedPropertyNames2.ts, 0, 16)) + + isIndexType(): this is IndexType; +>isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>IndexType : Symbol(IndexType, Decl(controlFlowComputedPropertyNames2.ts, 3, 1)) +} + +interface IndexType extends Type { +>IndexType : Symbol(IndexType, Decl(controlFlowComputedPropertyNames2.ts, 3, 1)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + kind: 1; +>kind : Symbol(IndexType.kind, Decl(controlFlowComputedPropertyNames2.ts, 5, 34)) + + type: Type; +>type : Symbol(IndexType.type, Decl(controlFlowComputedPropertyNames2.ts, 6, 10)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) +} + +function test1(types: Type[]) { +>test1 : Symbol(test1, Decl(controlFlowComputedPropertyNames2.ts, 8, 1)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 10, 15)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + for (let i = 0; i < types.length; i++) { +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) +>types.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 10, 15)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) + + const t = types[i].isIndexType() ? types[i].type : types[i]; // ok +>t : Symbol(t, Decl(controlFlowComputedPropertyNames2.ts, 12, 9)) +>types[i].isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 10, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) +>isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types[i].type : Symbol(IndexType.type, Decl(controlFlowComputedPropertyNames2.ts, 6, 10)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 10, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) +>type : Symbol(IndexType.type, Decl(controlFlowComputedPropertyNames2.ts, 6, 10)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 10, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 11, 10)) + } +} + +function test2(types: Type[]) { +>test2 : Symbol(test2, Decl(controlFlowComputedPropertyNames2.ts, 14, 1)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 16, 15)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + for (let i = 0; i < types.length; i++) { +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) +>types.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 16, 15)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) + + i++; +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) + + const t = types[i].isIndexType() ? types[i].type : types[i]; // error +>t : Symbol(t, Decl(controlFlowComputedPropertyNames2.ts, 19, 9)) +>types[i].isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 16, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) +>isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 16, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 16, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 17, 10)) + } +} + +function test3(types: Type[]) { +>test3 : Symbol(test3, Decl(controlFlowComputedPropertyNames2.ts, 21, 1)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 23, 15)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + for ( + let i = 0; +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) + + i < types.length; +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) +>types.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 23, 15)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + + types[i].isIndexType() ? types[i].type : types[i], i++ // error +>types[i].isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 23, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) +>isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 23, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 23, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 25, 7)) + + ) {} +} + +function test4(types: Type[]) { +>test4 : Symbol(test4, Decl(controlFlowComputedPropertyNames2.ts, 29, 1)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 31, 15)) +>Type : Symbol(Type, Decl(controlFlowComputedPropertyNames2.ts, 0, 0)) + + for ( + let i = 0; +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) + + types[i].isIndexType() ? types[i].type : types[i], i < types.length; // error +>types[i].isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 31, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) +>isIndexType : Symbol(Type.isIndexType, Decl(controlFlowComputedPropertyNames2.ts, 1, 15)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 31, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 31, 15)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) +>types.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>types : Symbol(types, Decl(controlFlowComputedPropertyNames2.ts, 31, 15)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + + i++ +>i : Symbol(i, Decl(controlFlowComputedPropertyNames2.ts, 33, 7)) + + ) {} +} + diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames2.types b/tests/baselines/reference/controlFlowComputedPropertyNames2.types new file mode 100644 index 0000000000000..133f9790e44e3 --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames2.types @@ -0,0 +1,285 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts] //// + +=== controlFlowComputedPropertyNames2.ts === +interface Type { + kind: number; +>kind : number +> : ^^^^^^ + + isIndexType(): this is IndexType; +>isIndexType : () => this is IndexType +> : ^^^^^^ +} + +interface IndexType extends Type { + kind: 1; +>kind : 1 +> : ^ + + type: Type; +>type : Type +> : ^^^^ +} + +function test1(types: Type[]) { +>test1 : (types: Type[]) => void +> : ^ ^^ ^^^^^^^^^ +>types : Type[] +> : ^^^^^^ + + for (let i = 0; i < types.length; i++) { +>i : number +> : ^^^^^^ +>0 : 0 +> : ^ +>i < types.length : boolean +> : ^^^^^^^ +>i : number +> : ^^^^^^ +>types.length : number +> : ^^^^^^ +>types : Type[] +> : ^^^^^^ +>length : number +> : ^^^^^^ +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + const t = types[i].isIndexType() ? types[i].type : types[i]; // ok +>t : Type +> : ^^^^ +>types[i].isIndexType() ? types[i].type : types[i] : Type +> : ^^^^ +>types[i].isIndexType() : boolean +> : ^^^^^^^ +>types[i].isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i].type : Type +> : ^^^^ +>types[i] : IndexType +> : ^^^^^^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>type : Type +> : ^^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ + } +} + +function test2(types: Type[]) { +>test2 : (types: Type[]) => void +> : ^ ^^ ^^^^^^^^^ +>types : Type[] +> : ^^^^^^ + + for (let i = 0; i < types.length; i++) { +>i : number +> : ^^^^^^ +>0 : 0 +> : ^ +>i < types.length : boolean +> : ^^^^^^^ +>i : number +> : ^^^^^^ +>types.length : number +> : ^^^^^^ +>types : Type[] +> : ^^^^^^ +>length : number +> : ^^^^^^ +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + i++; +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + const t = types[i].isIndexType() ? types[i].type : types[i]; // error +>t : any +> : ^^^ +>types[i].isIndexType() ? types[i].type : types[i] : any +> : ^^^ +>types[i].isIndexType() : boolean +> : ^^^^^^^ +>types[i].isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i].type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ + } +} + +function test3(types: Type[]) { +>test3 : (types: Type[]) => void +> : ^ ^^ ^^^^^^^^^ +>types : Type[] +> : ^^^^^^ + + for ( + let i = 0; +>i : number +> : ^^^^^^ +>0 : 0 +> : ^ + + i < types.length; +>i < types.length : boolean +> : ^^^^^^^ +>i : number +> : ^^^^^^ +>types.length : number +> : ^^^^^^ +>types : Type[] +> : ^^^^^^ +>length : number +> : ^^^^^^ + + types[i].isIndexType() ? types[i].type : types[i], i++ // error +>types[i].isIndexType() ? types[i].type : types[i], i++ : number +> : ^^^^^^ +>types[i].isIndexType() ? types[i].type : types[i] : any +> : ^^^ +>types[i].isIndexType() : boolean +> : ^^^^^^^ +>types[i].isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i].type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + ) {} +} + +function test4(types: Type[]) { +>test4 : (types: Type[]) => void +> : ^ ^^ ^^^^^^^^^ +>types : Type[] +> : ^^^^^^ + + for ( + let i = 0; +>i : number +> : ^^^^^^ +>0 : 0 +> : ^ + + types[i].isIndexType() ? types[i].type : types[i], i < types.length; // error +>types[i].isIndexType() ? types[i].type : types[i], i < types.length : boolean +> : ^^^^^^^ +>types[i].isIndexType() ? types[i].type : types[i] : any +> : ^^^ +>types[i].isIndexType() : boolean +> : ^^^^^^^ +>types[i].isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>isIndexType : () => this is IndexType +> : ^^^^^^ +>types[i].type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>type : any +> : ^^^ +>types[i] : Type +> : ^^^^ +>types : Type[] +> : ^^^^^^ +>i : number +> : ^^^^^^ +>i < types.length : boolean +> : ^^^^^^^ +>i : number +> : ^^^^^^ +>types.length : number +> : ^^^^^^ +>types : Type[] +> : ^^^^^^ +>length : number +> : ^^^^^^ + + i++ +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + ) {} +} + diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames3.symbols b/tests/baselines/reference/controlFlowComputedPropertyNames3.symbols new file mode 100644 index 0000000000000..06f1c505c5005 --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames3.symbols @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts] //// + +=== controlFlowComputedPropertyNames3.ts === +function goThroughArray() { +>goThroughArray : Symbol(goThroughArray, Decl(controlFlowComputedPropertyNames3.ts, 0, 0)) + + const a: number[] = [1, 2, 3]; +>a : Symbol(a, Decl(controlFlowComputedPropertyNames3.ts, 1, 7)) + + for (let i = 0; i < a.length; i++) { +>i : Symbol(i, Decl(controlFlowComputedPropertyNames3.ts, 2, 10)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames3.ts, 2, 10)) +>a.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>a : Symbol(a, Decl(controlFlowComputedPropertyNames3.ts, 1, 7)) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames3.ts, 2, 10)) + + if (typeof a[i] === "number") { +>a : Symbol(a, Decl(controlFlowComputedPropertyNames3.ts, 1, 7)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames3.ts, 2, 10)) + + a[i]++; +>a : Symbol(a, Decl(controlFlowComputedPropertyNames3.ts, 1, 7)) +>i : Symbol(i, Decl(controlFlowComputedPropertyNames3.ts, 2, 10)) + } + } +} + diff --git a/tests/baselines/reference/controlFlowComputedPropertyNames3.types b/tests/baselines/reference/controlFlowComputedPropertyNames3.types new file mode 100644 index 0000000000000..08039a8d85523 --- /dev/null +++ b/tests/baselines/reference/controlFlowComputedPropertyNames3.types @@ -0,0 +1,66 @@ +//// [tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts] //// + +=== controlFlowComputedPropertyNames3.ts === +function goThroughArray() { +>goThroughArray : () => void +> : ^^^^^^^^^^ + + const a: number[] = [1, 2, 3]; +>a : number[] +> : ^^^^^^^^ +>[1, 2, 3] : number[] +> : ^^^^^^^^ +>1 : 1 +> : ^ +>2 : 2 +> : ^ +>3 : 3 +> : ^ + + for (let i = 0; i < a.length; i++) { +>i : number +> : ^^^^^^ +>0 : 0 +> : ^ +>i < a.length : boolean +> : ^^^^^^^ +>i : number +> : ^^^^^^ +>a.length : number +> : ^^^^^^ +>a : number[] +> : ^^^^^^^^ +>length : number +> : ^^^^^^ +>i++ : number +> : ^^^^^^ +>i : number +> : ^^^^^^ + + if (typeof a[i] === "number") { +>typeof a[i] === "number" : boolean +> : ^^^^^^^ +>typeof a[i] : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>a[i] : number | undefined +> : ^^^^^^^^^^^^^^^^^^ +>a : number[] +> : ^^^^^^^^ +>i : number +> : ^^^^^^ +>"number" : "number" +> : ^^^^^^^^ + + a[i]++; +>a[i]++ : number +> : ^^^^^^ +>a[i] : number +> : ^^^^^^ +>a : number[] +> : ^^^^^^^^ +>i : number +> : ^^^^^^ + } + } +} + diff --git a/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts new file mode 100644 index 0000000000000..25c042b492a32 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames2.ts @@ -0,0 +1,41 @@ +// @strict: true +// @noEmit: true + +interface Type { + kind: number; + isIndexType(): this is IndexType; +} + +interface IndexType extends Type { + kind: 1; + type: Type; +} + +function test1(types: Type[]) { + for (let i = 0; i < types.length; i++) { + const t = types[i].isIndexType() ? types[i].type : types[i]; // ok + } +} + +function test2(types: Type[]) { + for (let i = 0; i < types.length; i++) { + i++; + const t = types[i].isIndexType() ? types[i].type : types[i]; // error + } +} + +function test3(types: Type[]) { + for ( + let i = 0; + i < types.length; + types[i].isIndexType() ? types[i].type : types[i], i++ // error + ) {} +} + +function test4(types: Type[]) { + for ( + let i = 0; + types[i].isIndexType() ? types[i].type : types[i], i < types.length; // error + i++ + ) {} +} diff --git a/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts new file mode 100644 index 0000000000000..99974cd922599 --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowComputedPropertyNames3.ts @@ -0,0 +1,12 @@ +// @strict: true +// @noEmit: true +// @noUncheckedIndexedAccess: true + +function goThroughArray() { + const a: number[] = [1, 2, 3]; + for (let i = 0; i < a.length; i++) { + if (typeof a[i] === "number") { + a[i]++; + } + } +}