Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/compiler/resolutionCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
ModeAwareCache,
ModuleResolutionCache,
moduleResolutionNameAndModeGetter,
Mutable,
mutateMap,
noopFileWatcher,
normalizePath,
Expand Down Expand Up @@ -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<typeof primaryResult>).resolvedModule = resolvedModule;
primaryResult.failedLookupLocations = updateResolutionField(primaryResult.failedLookupLocations, failedLookupLocations);
primaryResult.affectingLocations = updateResolutionField(primaryResult.affectingLocations, affectingLocations);
primaryResult.resolutionDiagnostics = updateResolutionField(primaryResult.resolutionDiagnostics, resolutionDiagnostics);
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4801,6 +4801,8 @@ function getAssignmentTarget(node: Node): AssignmentTarget | undefined {
case SyntaxKind.ArrayLiteralExpression:
case SyntaxKind.SpreadElement:
case SyntaxKind.NonNullExpression:
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:
node = parent;
break;
case SyntaxKind.SpreadAssignment:
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -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 changes: 60 additions & 0 deletions tests/baselines/reference/usedBeforeAssignedTypeAssertion.js
Original file line number Diff line number Diff line change
@@ -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
}
57 changes: 57 additions & 0 deletions tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols
Original file line number Diff line number Diff line change
@@ -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 changes: 106 additions & 0 deletions tests/baselines/reference/usedBeforeAssignedTypeAssertion.types
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts
Original file line number Diff line number Diff line change
@@ -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
}
Loading