Fix "used before being assigned" error with type assertions in LHS

Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-07-25 18:34:27 +00:00
parent 2920760406
commit 1c4291ed96
6 changed files with 334 additions and 43 deletions

View File

@@ -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 */

View File

@@ -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;
(<any>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.
}

View File

@@ -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;
(<any>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
}

View File

@@ -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))
(<any>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))
}

View File

@@ -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
> : ^^^^^^
(<any>x) = 42; // Should not error - this is an assignment
>(<any>x) = 42 : 42
> : ^^
>(<any>x) : any
> : ^^^
><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
> : ^^^^^^
}

View File

@@ -0,0 +1,31 @@
// @strict: true
// Test case for type assertion (angle bracket syntax) - assignment should not error
function testTypeAssertion() {
let x: number;
(<any>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
}