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); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7a88ad19487a5..7dd454b2abdec 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4815,6 +4815,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: 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 diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt new file mode 100644 index 0000000000000..4cecead9a1617 --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.errors.txt @@ -0,0 +1,60 @@ +usedBeforeAssignedTypeAssertion.ts(28,6): error TS2588: Cannot assign to 'm' because it is a constant. +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 (3 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 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 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; + 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..1f389b350d62a --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.js @@ -0,0 +1,91 @@ +//// [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 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 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; + 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 case for const assignment via type assertion - should error +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; + 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..5a7306690f25c --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.symbols @@ -0,0 +1,94 @@ +//// [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 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 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, 41, 1)) + + let uninitialized: number; +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 45, 7)) + + return uninitialized; // Should error - never assigned +>uninitialized : Symbol(uninitialized, Decl(usedBeforeAssignedTypeAssertion.ts, 45, 7)) +} diff --git a/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types new file mode 100644 index 0000000000000..7c8511bff5a91 --- /dev/null +++ b/tests/baselines/reference/usedBeforeAssignedTypeAssertion.types @@ -0,0 +1,175 @@ +//// [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 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 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 +> : ^^^^^^^^^^^^ + + 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..77be4e15d7950 --- /dev/null +++ b/tests/cases/compiler/usedBeforeAssignedTypeAssertion.ts @@ -0,0 +1,50 @@ +// @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 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 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; + return uninitialized; // Should error - never assigned +} \ No newline at end of file