mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 12:32:42 -05:00
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:
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
60
tests/baselines/reference/usedBeforeAssignedTypeAssertion.js
Normal file
60
tests/baselines/reference/usedBeforeAssignedTypeAssertion.js
Normal 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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
106
tests/baselines/reference/usedBeforeAssignedTypeAssertion.types
Normal file
106
tests/baselines/reference/usedBeforeAssignedTypeAssertion.types
Normal 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
|
||||
> : ^^^^^^
|
||||
}
|
||||
31
tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts
Normal file
31
tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user