From 2920760406510315aece6ae0518e6304eff3b259 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:16:06 +0000 Subject: [PATCH 1/6] Initial plan From 1c4291ed96c109d025ff2d7ad438f8c59b4cf182 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:34:27 +0000 Subject: [PATCH 2/6] Fix "used before being assigned" error with type assertions in LHS Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- src/compiler/utilities.ts | 88 ++++++++------- ...usedBeforeAssignedTypeAssertion.errors.txt | 35 ++++++ .../usedBeforeAssignedTypeAssertion.js | 60 ++++++++++ .../usedBeforeAssignedTypeAssertion.symbols | 57 ++++++++++ .../usedBeforeAssignedTypeAssertion.types | 106 ++++++++++++++++++ .../usedBeforeAssignedTypeAssertion.ts | 31 +++++ 6 files changed, 334 insertions(+), 43 deletions(-) create mode 100644 tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt create mode 100644 tests/baselines/reference/usedBeforeAssignedTypeAssertion.js create mode 100644 tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols create mode 100644 tests/baselines/reference/usedBeforeAssignedTypeAssertion.types create mode 100644 tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4687bb8796fad..c6703ab7bf7a6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4780,49 +4780,51 @@ type AssignmentTarget = | PostfixUnaryExpression | ForInOrOfStatement; -function getAssignmentTarget(node: Node): AssignmentTarget | undefined { - let parent = node.parent; - while (true) { - switch (parent.kind) { - case SyntaxKind.BinaryExpression: - const binaryExpression = parent as BinaryExpression; - const binaryOperator = binaryExpression.operatorToken.kind; - return isAssignmentOperator(binaryOperator) && binaryExpression.left === node ? binaryExpression : undefined; - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - const unaryExpression = parent as PrefixUnaryExpression | PostfixUnaryExpression; - const unaryOperator = unaryExpression.operator; - return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? unaryExpression : undefined; - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - const forInOrOfStatement = parent as ForInOrOfStatement; - return forInOrOfStatement.initializer === node ? forInOrOfStatement : undefined; - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.SpreadElement: - case SyntaxKind.NonNullExpression: - node = parent; - break; - case SyntaxKind.SpreadAssignment: - node = parent.parent; - break; - case SyntaxKind.ShorthandPropertyAssignment: - if ((parent as ShorthandPropertyAssignment).name !== node) { - return undefined; - } - node = parent.parent; - break; - case SyntaxKind.PropertyAssignment: - if ((parent as PropertyAssignment).name === node) { - return undefined; - } - node = parent.parent; - break; - default: - return undefined; - } - parent = node.parent; - } +function getAssignmentTarget(node: Node): AssignmentTarget | undefined { + let parent = node.parent; + while (true) { + switch (parent.kind) { + case SyntaxKind.BinaryExpression: + const binaryExpression = parent as BinaryExpression; + const binaryOperator = binaryExpression.operatorToken.kind; + return isAssignmentOperator(binaryOperator) && binaryExpression.left === node ? binaryExpression : undefined; + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + const unaryExpression = parent as PrefixUnaryExpression | PostfixUnaryExpression; + const unaryOperator = unaryExpression.operator; + return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? unaryExpression : undefined; + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + const forInOrOfStatement = parent as ForInOrOfStatement; + return forInOrOfStatement.initializer === node ? forInOrOfStatement : undefined; + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.SpreadElement: + case SyntaxKind.NonNullExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + node = parent; + break; + case SyntaxKind.SpreadAssignment: + node = parent.parent; + break; + case SyntaxKind.ShorthandPropertyAssignment: + if ((parent as ShorthandPropertyAssignment).name !== node) { + return undefined; + } + node = parent.parent; + break; + case SyntaxKind.PropertyAssignment: + if ((parent as PropertyAssignment).name === node) { + return undefined; + } + node = parent.parent; + break; + default: + return undefined; + } + parent = node.parent; + } } /** @internal */ diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt new file mode 100644 index 0000000000000..90453e700831d --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt @@ -0,0 +1,35 @@ +usedBeforeAssignedTypeAssertion.ts(28,12): error TS2454: Variable 'uninitialized' is used before being assigned. + + +==== usedBeforeAssignedTypeAssertion.ts (1 errors) ==== + // Test case for type assertion (angle bracket syntax) - assignment should not error + function testTypeAssertion() { + let x: number; + (x) = 42; // Should not error - this is an assignment + } + + // Test case for 'as' expression - assignment should not error + function testAsExpression() { + let y: number; + (y as any) = 42; // Should not error - this is an assignment + } + + // Test case for parenthesized expression (should already work) + function testParentheses() { + let z: number; + (z) = 42; // Should not error - this is an assignment + } + + // Test case with nested type assertions + function testNested() { + let nested: any; + ((nested as any) as unknown) = "test"; // Should not error + } + + // Test cases that should still produce errors for proper context + function shouldStillError() { + let uninitialized: number; + return uninitialized; // Should error - never assigned + ~~~~~~~~~~~~~ +!!! error TS2454: Variable 'uninitialized' is used before being assigned. + } \ No newline at end of file diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js new file mode 100644 index 0000000000000..1e72ae11dbd7d --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts] //// + +//// [usedBeforeAssignedTypeAssertion.ts] +// Test case for type assertion (angle bracket syntax) - assignment should not error +function testTypeAssertion() { + let x: number; + (x) = 42; // Should not error - this is an assignment +} + +// Test case for 'as' expression - assignment should not error +function testAsExpression() { + let y: number; + (y as any) = 42; // Should not error - this is an assignment +} + +// Test case for parenthesized expression (should already work) +function testParentheses() { + let z: number; + (z) = 42; // Should not error - this is an assignment +} + +// Test case with nested type assertions +function testNested() { + let nested: any; + ((nested as any) as unknown) = "test"; // Should not error +} + +// Test cases that should still produce errors for proper context +function shouldStillError() { + let uninitialized: number; + return uninitialized; // Should error - never assigned +} + +//// [usedBeforeAssignedTypeAssertion.js] +"use strict"; +// Test case for type assertion (angle bracket syntax) - assignment should not error +function testTypeAssertion() { + var x; + x = 42; // Should not error - this is an assignment +} +// Test case for 'as' expression - assignment should not error +function testAsExpression() { + var y; + y = 42; // Should not error - this is an assignment +} +// Test case for parenthesized expression (should already work) +function testParentheses() { + var z; + (z) = 42; // Should not error - this is an assignment +} +// Test case with nested type assertions +function testNested() { + var nested; + nested = "test"; // Should not error +} +// Test cases that should still produce errors for proper context +function shouldStillError() { + var uninitialized; + return uninitialized; // Should error - never assigned +} diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols new file mode 100644 index 0000000000000..e9ebf35a4acd0 --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols @@ -0,0 +1,57 @@ +//// [tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts] //// + +=== usedBeforeAssignedTypeAssertion.ts === +// Test case for type assertion (angle bracket syntax) - assignment should not error +function testTypeAssertion() { +>testTypeAssertion : Symbol(testTypeAssertion, Decl(usedBeforeAssignedTypeAssertion.ts, 0, 0)) + + let x: number; +>x : Symbol(x, Decl(usedBeforeAssignedTypeAssertion.ts, 2, 7)) + + (x) = 42; // Should not error - this is an assignment +>x : Symbol(x, Decl(usedBeforeAssignedTypeAssertion.ts, 2, 7)) +} + +// Test case for 'as' expression - assignment should not error +function testAsExpression() { +>testAsExpression : Symbol(testAsExpression, Decl(usedBeforeAssignedTypeAssertion.ts, 4, 1)) + + let y: number; +>y : Symbol(y, Decl(usedBeforeAssignedTypeAssertion.ts, 8, 7)) + + (y as any) = 42; // Should not error - this is an assignment +>y : Symbol(y, Decl(usedBeforeAssignedTypeAssertion.ts, 8, 7)) +} + +// Test case for parenthesized expression (should already work) +function testParentheses() { +>testParentheses : Symbol(testParentheses, Decl(usedBeforeAssignedTypeAssertion.ts, 10, 1)) + + let z: number; +>z : Symbol(z, Decl(usedBeforeAssignedTypeAssertion.ts, 14, 7)) + + (z) = 42; // Should not error - this is an assignment +>z : Symbol(z, Decl(usedBeforeAssignedTypeAssertion.ts, 14, 7)) +} + +// Test case with nested type assertions +function testNested() { +>testNested : Symbol(testNested, Decl(usedBeforeAssignedTypeAssertion.ts, 16, 1)) + + let nested: any; +>nested : Symbol(nested, Decl(usedBeforeAssignedTypeAssertion.ts, 20, 7)) + + ((nested as any) as unknown) = "test"; // Should not error +>nested : Symbol(nested, Decl(usedBeforeAssignedTypeAssertion.ts, 20, 7)) +} + +// Test cases that should still produce errors for proper context +function shouldStillError() { +>shouldStillError : Symbol(shouldStillError, Decl(usedBeforeAssignedTypeAssertion.ts, 22, 1)) + + let uninitialized: number; +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 7)) + + return uninitialized; // Should error - never assigned +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 7)) +} diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types new file mode 100644 index 0000000000000..9b38ec1898dbe --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types @@ -0,0 +1,106 @@ +//// [tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts] //// + +=== usedBeforeAssignedTypeAssertion.ts === +// Test case for type assertion (angle bracket syntax) - assignment should not error +function testTypeAssertion() { +>testTypeAssertion : () => void +> : ^^^^^^^^^^ + + let x: number; +>x : number +> : ^^^^^^ + + (x) = 42; // Should not error - this is an assignment +>(x) = 42 : 42 +> : ^^ +>(x) : any +> : ^^^ +>x : any +> : ^^^ +>x : number +> : ^^^^^^ +>42 : 42 +> : ^^ +} + +// Test case for 'as' expression - assignment should not error +function testAsExpression() { +>testAsExpression : () => void +> : ^^^^^^^^^^ + + let y: number; +>y : number +> : ^^^^^^ + + (y as any) = 42; // Should not error - this is an assignment +>(y as any) = 42 : 42 +> : ^^ +>(y as any) : any +> : ^^^ +>y as any : any +> : ^^^ +>y : number +> : ^^^^^^ +>42 : 42 +> : ^^ +} + +// Test case for parenthesized expression (should already work) +function testParentheses() { +>testParentheses : () => void +> : ^^^^^^^^^^ + + let z: number; +>z : number +> : ^^^^^^ + + (z) = 42; // Should not error - this is an assignment +>(z) = 42 : 42 +> : ^^ +>(z) : number +> : ^^^^^^ +>z : number +> : ^^^^^^ +>42 : 42 +> : ^^ +} + +// Test case with nested type assertions +function testNested() { +>testNested : () => void +> : ^^^^^^^^^^ + + let nested: any; +>nested : any +> : ^^^ + + ((nested as any) as unknown) = "test"; // Should not error +>((nested as any) as unknown) = "test" : "test" +> : ^^^^^^ +>((nested as any) as unknown) : unknown +> : ^^^^^^^ +>(nested as any) as unknown : unknown +> : ^^^^^^^ +>(nested as any) : any +> : ^^^ +>nested as any : any +> : ^^^ +>nested : any +> : ^^^ +>"test" : "test" +> : ^^^^^^ +} + +// Test cases that should still produce errors for proper context +function shouldStillError() { +>shouldStillError : () => number +> : ^^^^^^^^^^^^ + + let uninitialized: number; +>uninitialized : number +> : ^^^^^^ + + return uninitialized; // Should error - never assigned +>uninitialized : number +> : ^^^^^^ +} diff --git a/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts new file mode 100644 index 0000000000000..a5de18c18bc89 --- /dev/null +++ b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts @@ -0,0 +1,31 @@ +// @strict: true + +// Test case for type assertion (angle bracket syntax) - assignment should not error +function testTypeAssertion() { + let x: number; + (x) = 42; // Should not error - this is an assignment +} + +// Test case for 'as' expression - assignment should not error +function testAsExpression() { + let y: number; + (y as any) = 42; // Should not error - this is an assignment +} + +// Test case for parenthesized expression (should already work) +function testParentheses() { + let z: number; + (z) = 42; // Should not error - this is an assignment +} + +// Test case with nested type assertions +function testNested() { + let nested: any; + ((nested as any) as unknown) = "test"; // Should not error +} + +// Test cases that should still produce errors for proper context +function shouldStillError() { + let uninitialized: number; + return uninitialized; // Should error - never assigned +} \ No newline at end of file From 931430c3354ff40de1dfe7bda3fe73cd0524adc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:57:55 +0000 Subject: [PATCH 3/6] Accept baseline changes and complete fix for type assertion assignment issue Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- src/compiler/utilities.ts | 90 +++++++++---------- ...sNameReferencesInStaticElements.errors.txt | 21 +++++ 2 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 tests/baselines/reference/classNameReferencesInStaticElements.errors.txt diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c6703ab7bf7a6..a78643db4c0c6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4780,51 +4780,51 @@ type AssignmentTarget = | PostfixUnaryExpression | ForInOrOfStatement; -function getAssignmentTarget(node: Node): AssignmentTarget | undefined { - let parent = node.parent; - while (true) { - switch (parent.kind) { - case SyntaxKind.BinaryExpression: - const binaryExpression = parent as BinaryExpression; - const binaryOperator = binaryExpression.operatorToken.kind; - return isAssignmentOperator(binaryOperator) && binaryExpression.left === node ? binaryExpression : undefined; - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - const unaryExpression = parent as PrefixUnaryExpression | PostfixUnaryExpression; - const unaryOperator = unaryExpression.operator; - return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? unaryExpression : undefined; - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - const forInOrOfStatement = parent as ForInOrOfStatement; - return forInOrOfStatement.initializer === node ? forInOrOfStatement : undefined; - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.SpreadElement: - case SyntaxKind.NonNullExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - node = parent; - break; - case SyntaxKind.SpreadAssignment: - node = parent.parent; - break; - case SyntaxKind.ShorthandPropertyAssignment: - if ((parent as ShorthandPropertyAssignment).name !== node) { - return undefined; - } - node = parent.parent; - break; - case SyntaxKind.PropertyAssignment: - if ((parent as PropertyAssignment).name === node) { - return undefined; - } - node = parent.parent; - break; - default: - return undefined; - } - parent = node.parent; - } +function getAssignmentTarget(node: Node): AssignmentTarget | undefined { + let parent = node.parent; + while (true) { + switch (parent.kind) { + case SyntaxKind.BinaryExpression: + const binaryExpression = parent as BinaryExpression; + const binaryOperator = binaryExpression.operatorToken.kind; + return isAssignmentOperator(binaryOperator) && binaryExpression.left === node ? binaryExpression : undefined; + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + const unaryExpression = parent as PrefixUnaryExpression | PostfixUnaryExpression; + const unaryOperator = unaryExpression.operator; + return unaryOperator === SyntaxKind.PlusPlusToken || unaryOperator === SyntaxKind.MinusMinusToken ? unaryExpression : undefined; + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + const forInOrOfStatement = parent as ForInOrOfStatement; + return forInOrOfStatement.initializer === node ? forInOrOfStatement : undefined; + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.SpreadElement: + case SyntaxKind.NonNullExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + node = parent; + break; + case SyntaxKind.SpreadAssignment: + node = parent.parent; + break; + case SyntaxKind.ShorthandPropertyAssignment: + if ((parent as ShorthandPropertyAssignment).name !== node) { + return undefined; + } + node = parent.parent; + break; + case SyntaxKind.PropertyAssignment: + if ((parent as PropertyAssignment).name === node) { + return undefined; + } + node = parent.parent; + break; + default: + return undefined; + } + parent = node.parent; + } } /** @internal */ diff --git a/tests/baselines/reference/classNameReferencesInStaticElements.errors.txt b/tests/baselines/reference/classNameReferencesInStaticElements.errors.txt new file mode 100644 index 0000000000000..43328c9e1f238 --- /dev/null +++ b/tests/baselines/reference/classNameReferencesInStaticElements.errors.txt @@ -0,0 +1,21 @@ +classNameReferencesInStaticElements.ts(12,2): error TS2629: Cannot assign to 'Foo' because it is a class. + + +==== classNameReferencesInStaticElements.ts (1 errors) ==== + // https://github.com/microsoft/TypeScript/issues/54607 + class Foo { + static { console.log(this, Foo) } + static x = () => { console.log(this, Foo) } + static y = function(this: unknown) { console.log(this, Foo) } + + #x() { console.log(Foo); } + x() { this.#x(); } + } + + const oldFoo = Foo; + (Foo as any) = null; + ~~~ +!!! error TS2629: Cannot assign to 'Foo' because it is a class. + oldFoo.x(); + oldFoo.y(); + new oldFoo().x(); \ No newline at end of file From 2aca21175e956fc7494ea5100e9d16009fca2ef3 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 25 Jul 2025 13:21:39 -0700 Subject: [PATCH 4/6] Fix self test --- src/compiler/resolutionCache.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 7bced48353b82..a8757512f4194 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -47,6 +47,7 @@ import { ModeAwareCache, ModuleResolutionCache, moduleResolutionNameAndModeGetter, + Mutable, mutateMap, noopFileWatcher, normalizePath, @@ -556,7 +557,7 @@ function resolveModuleNameUsingGlobalCache( ); if (resolvedModule) { // Modify existing resolution so its saved in the directory cache as well - (primaryResult.resolvedModule as any) = resolvedModule; + (primaryResult as Mutable).resolvedModule = resolvedModule; primaryResult.failedLookupLocations = updateResolutionField(primaryResult.failedLookupLocations, failedLookupLocations); primaryResult.affectingLocations = updateResolutionField(primaryResult.affectingLocations, affectingLocations); primaryResult.resolutionDiagnostics = updateResolutionField(primaryResult.resolutionDiagnostics, resolutionDiagnostics); From 060a88a38421cfbf4fa4cb9fac6915ee73585f61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:12:50 +0000 Subject: [PATCH 5/6] Add test case for const assignment via type assertion per RyanCavanaugh feedback Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- ...usedBeforeAssignedTypeAssertion.errors.txt | 13 ++++++++-- .../usedBeforeAssignedTypeAssertion.js | 11 +++++++++ .../usedBeforeAssignedTypeAssertion.symbols | 17 ++++++++++--- .../usedBeforeAssignedTypeAssertion.types | 24 +++++++++++++++++++ .../usedBeforeAssignedTypeAssertion.ts | 6 +++++ 5 files changed, 66 insertions(+), 5 deletions(-) diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt index 90453e700831d..2bd93515b3ff0 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt @@ -1,7 +1,8 @@ -usedBeforeAssignedTypeAssertion.ts(28,12): error TS2454: Variable 'uninitialized' is used before being assigned. +usedBeforeAssignedTypeAssertion.ts(28,6): error TS2588: Cannot assign to 'm' because it is a constant. +usedBeforeAssignedTypeAssertion.ts(34,12): error TS2454: Variable 'uninitialized' is used before being assigned. -==== usedBeforeAssignedTypeAssertion.ts (1 errors) ==== +==== usedBeforeAssignedTypeAssertion.ts (2 errors) ==== // Test case for type assertion (angle bracket syntax) - assignment should not error function testTypeAssertion() { let x: number; @@ -26,6 +27,14 @@ usedBeforeAssignedTypeAssertion.ts(28,12): error TS2454: Variable 'uninitialized ((nested as any) as unknown) = "test"; // Should not error } + // Test case for const assignment via type assertion - should error + function testConstAssignment() { + const m = 32; + (m as any) = 16; // Should error - cannot assign to const + ~ +!!! error TS2588: Cannot assign to 'm' because it is a constant. + } + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number; diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js index 1e72ae11dbd7d..d434d100a2300 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js @@ -25,6 +25,12 @@ function testNested() { ((nested as any) as unknown) = "test"; // Should not error } +// Test case for const assignment via type assertion - should error +function testConstAssignment() { + const m = 32; + (m as any) = 16; // Should error - cannot assign to const +} + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number; @@ -53,6 +59,11 @@ function testNested() { var nested; nested = "test"; // Should not error } +// Test case for const assignment via type assertion - should error +function testConstAssignment() { + var m = 32; + m = 16; // Should error - cannot assign to const +} // Test cases that should still produce errors for proper context function shouldStillError() { var uninitialized; diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols index e9ebf35a4acd0..c4c3f7923fbe3 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols @@ -45,13 +45,24 @@ function testNested() { >nested : Symbol(nested, Decl(usedBeforeAssignedTypeAssertion.ts, 20, 7)) } +// Test case for const assignment via type assertion - should error +function testConstAssignment() { +>testConstAssignment : Symbol(testConstAssignment, Decl(usedBeforeAssignedTypeAssertion.ts, 22, 1)) + + const m = 32; +>m : Symbol(m, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 9)) + + (m as any) = 16; // Should error - cannot assign to const +>m : Symbol(m, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 9)) +} + // Test cases that should still produce errors for proper context function shouldStillError() { ->shouldStillError : Symbol(shouldStillError, Decl(usedBeforeAssignedTypeAssertion.ts, 22, 1)) +>shouldStillError : Symbol(shouldStillError, Decl(usedBeforeAssignedTypeAssertion.ts, 28, 1)) let uninitialized: number; ->uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 7)) +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 7)) return uninitialized; // Should error - never assigned ->uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 7)) +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 7)) } diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types index 9b38ec1898dbe..da01dc01ab294 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types @@ -91,6 +91,30 @@ function testNested() { > : ^^^^^^ } +// Test case for const assignment via type assertion - should error +function testConstAssignment() { +>testConstAssignment : () => void +> : ^^^^^^^^^^ + + const m = 32; +>m : 32 +> : ^^ +>32 : 32 +> : ^^ + + (m as any) = 16; // Should error - cannot assign to const +>(m as any) = 16 : 16 +> : ^^ +>(m as any) : any +> : ^^^ +>m as any : any +> : ^^^ +>m : any +> : ^^^ +>16 : 16 +> : ^^ +} + // Test cases that should still produce errors for proper context function shouldStillError() { >shouldStillError : () => number diff --git a/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts index a5de18c18bc89..d187950789a5a 100644 --- a/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts +++ b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts @@ -24,6 +24,12 @@ function testNested() { ((nested as any) as unknown) = "test"; // Should not error } +// Test case for const assignment via type assertion - should error +function testConstAssignment() { + const m = 32; + (m as any) = 16; // Should error - cannot assign to const +} + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number; From a6f8898ee59302e2557f554c06b45fa2b5fff654 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:18:08 +0000 Subject: [PATCH 6/6] Add test case for readonly property assignment via type assertion per jakebailey feedback Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- ...usedBeforeAssignedTypeAssertion.errors.txt | 20 ++++++++- .../usedBeforeAssignedTypeAssertion.js | 20 +++++++++ .../usedBeforeAssignedTypeAssertion.symbols | 32 +++++++++++-- .../usedBeforeAssignedTypeAssertion.types | 45 +++++++++++++++++++ .../usedBeforeAssignedTypeAssertion.ts | 13 ++++++ 5 files changed, 125 insertions(+), 5 deletions(-) diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt index 2bd93515b3ff0..4cecead9a1617 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt @@ -1,8 +1,9 @@ usedBeforeAssignedTypeAssertion.ts(28,6): error TS2588: Cannot assign to 'm' because it is a constant. -usedBeforeAssignedTypeAssertion.ts(34,12): error TS2454: Variable 'uninitialized' is used before being assigned. +usedBeforeAssignedTypeAssertion.ts(41,10): error TS2540: Cannot assign to 'prop' because it is a read-only property. +usedBeforeAssignedTypeAssertion.ts(47,12): error TS2454: Variable 'uninitialized' is used before being assigned. -==== usedBeforeAssignedTypeAssertion.ts (2 errors) ==== +==== usedBeforeAssignedTypeAssertion.ts (3 errors) ==== // Test case for type assertion (angle bracket syntax) - assignment should not error function testTypeAssertion() { let x: number; @@ -35,6 +36,21 @@ usedBeforeAssignedTypeAssertion.ts(34,12): error TS2454: Variable 'uninitialized !!! error TS2588: Cannot assign to 'm' because it is a constant. } + // Test case for readonly property assignment via type assertion - should error + function testReadonlyPropertyAssignment() { + interface ReadonlyInterface { + readonly prop: number; + } + + let obj: ReadonlyInterface; + obj = { prop: 42 }; + + // Should error - cannot assign to readonly property, even through type assertion + (obj.prop as any) = 100; + ~~~~ +!!! error TS2540: Cannot assign to 'prop' because it is a read-only property. + } + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number; diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js index d434d100a2300..1f389b350d62a 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js @@ -31,6 +31,19 @@ function testConstAssignment() { (m as any) = 16; // Should error - cannot assign to const } +// Test case for readonly property assignment via type assertion - should error +function testReadonlyPropertyAssignment() { + interface ReadonlyInterface { + readonly prop: number; + } + + let obj: ReadonlyInterface; + obj = { prop: 42 }; + + // Should error - cannot assign to readonly property, even through type assertion + (obj.prop as any) = 100; +} + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number; @@ -64,6 +77,13 @@ function testConstAssignment() { var m = 32; m = 16; // Should error - cannot assign to const } +// Test case for readonly property assignment via type assertion - should error +function testReadonlyPropertyAssignment() { + var obj; + obj = { prop: 42 }; + // Should error - cannot assign to readonly property, even through type assertion + obj.prop = 100; +} // Test cases that should still produce errors for proper context function shouldStillError() { var uninitialized; diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols index c4c3f7923fbe3..5a7306690f25c 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols @@ -56,13 +56,39 @@ function testConstAssignment() { >m : Symbol(m, Decl(usedBeforeAssignedTypeAssertion.ts, 26, 9)) } +// Test case for readonly property assignment via type assertion - should error +function testReadonlyPropertyAssignment() { +>testReadonlyPropertyAssignment : Symbol(testReadonlyPropertyAssignment, Decl(usedBeforeAssignedTypeAssertion.ts, 28, 1)) + + interface ReadonlyInterface { +>ReadonlyInterface : Symbol(ReadonlyInterface, Decl(usedBeforeAssignedTypeAssertion.ts, 31, 43)) + + readonly prop: number; +>prop : Symbol(ReadonlyInterface.prop, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 33)) + } + + let obj: ReadonlyInterface; +>obj : Symbol(obj, Decl(usedBeforeAssignedTypeAssertion.ts, 36, 7)) +>ReadonlyInterface : Symbol(ReadonlyInterface, Decl(usedBeforeAssignedTypeAssertion.ts, 31, 43)) + + obj = { prop: 42 }; +>obj : Symbol(obj, Decl(usedBeforeAssignedTypeAssertion.ts, 36, 7)) +>prop : Symbol(prop, Decl(usedBeforeAssignedTypeAssertion.ts, 37, 11)) + + // Should error - cannot assign to readonly property, even through type assertion + (obj.prop as any) = 100; +>obj.prop : Symbol(ReadonlyInterface.prop, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 33)) +>obj : Symbol(obj, Decl(usedBeforeAssignedTypeAssertion.ts, 36, 7)) +>prop : Symbol(ReadonlyInterface.prop, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 33)) +} + // Test cases that should still produce errors for proper context function shouldStillError() { ->shouldStillError : Symbol(shouldStillError, Decl(usedBeforeAssignedTypeAssertion.ts, 28, 1)) +>shouldStillError : Symbol(shouldStillError, Decl(usedBeforeAssignedTypeAssertion.ts, 41, 1)) let uninitialized: number; ->uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 7)) +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 45, 7)) return uninitialized; // Should error - never assigned ->uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 32, 7)) +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 45, 7)) } diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types index da01dc01ab294..7c8511bff5a91 100644 --- a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types @@ -115,6 +115,51 @@ function testConstAssignment() { > : ^^ } +// Test case for readonly property assignment via type assertion - should error +function testReadonlyPropertyAssignment() { +>testReadonlyPropertyAssignment : () => void +> : ^^^^^^^^^^ + + interface ReadonlyInterface { + readonly prop: number; +>prop : number +> : ^^^^^^ + } + + let obj: ReadonlyInterface; +>obj : ReadonlyInterface +> : ^^^^^^^^^^^^^^^^^ + + obj = { prop: 42 }; +>obj = { prop: 42 } : { prop: number; } +> : ^^^^^^^^^^^^^^^^^ +>obj : ReadonlyInterface +> : ^^^^^^^^^^^^^^^^^ +>{ prop: 42 } : { prop: number; } +> : ^^^^^^^^^^^^^^^^^ +>prop : number +> : ^^^^^^ +>42 : 42 +> : ^^ + + // Should error - cannot assign to readonly property, even through type assertion + (obj.prop as any) = 100; +>(obj.prop as any) = 100 : 100 +> : ^^^ +>(obj.prop as any) : any +> : ^^^ +>obj.prop as any : any +> : ^^^ +>obj.prop : any +> : ^^^ +>obj : ReadonlyInterface +> : ^^^^^^^^^^^^^^^^^ +>prop : any +> : ^^^ +>100 : 100 +> : ^^^ +} + // Test cases that should still produce errors for proper context function shouldStillError() { >shouldStillError : () => number diff --git a/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts index d187950789a5a..77be4e15d7950 100644 --- a/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts +++ b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts @@ -30,6 +30,19 @@ function testConstAssignment() { (m as any) = 16; // Should error - cannot assign to const } +// Test case for readonly property assignment via type assertion - should error +function testReadonlyPropertyAssignment() { + interface ReadonlyInterface { + readonly prop: number; + } + + let obj: ReadonlyInterface; + obj = { prop: 42 }; + + // Should error - cannot assign to readonly property, even through type assertion + (obj.prop as any) = 100; +} + // Test cases that should still produce errors for proper context function shouldStillError() { let uninitialized: number;