From 9668094ea4c9c43a3b9dac6c5d5b825d70e5518c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 23 Dec 2025 07:07:57 -1000 Subject: [PATCH 1/3] Check class symbol instead of declaration --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fa2a56e2fdfa6..d9542b567bbe3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34416,10 +34416,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop) && (isThisProperty(location) || isThisInitializedObjectBindingExpression(location) || isObjectBindingPattern(location.parent) && isThisInitializedDeclaration(location.parent.parent)) ) { - const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!); - if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(location)) { + const parentSymbol = getParentOfSymbol(prop); + if (parentSymbol && parentSymbol.flags&SymbolFlags.Class && isNodeUsedDuringClassInitialization(location)) { if (errorNode) { - error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)); + error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), symbolToString(parentSymbol)); } return false; } From 8424842a812a837a94bf2991520413a6716ae67b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 23 Dec 2025 07:08:07 -1000 Subject: [PATCH 2/3] Add regression test --- .../errorInUnnamedClassExpression.errors.txt | 21 ++++++++++++++ .../errorInUnnamedClassExpression.symbols | 19 +++++++++++++ .../errorInUnnamedClassExpression.types | 28 +++++++++++++++++++ .../compiler/errorInUnnamedClassExpression.ts | 11 ++++++++ 4 files changed, 79 insertions(+) create mode 100644 tests/baselines/reference/errorInUnnamedClassExpression.errors.txt create mode 100644 tests/baselines/reference/errorInUnnamedClassExpression.symbols create mode 100644 tests/baselines/reference/errorInUnnamedClassExpression.types create mode 100644 tests/cases/compiler/errorInUnnamedClassExpression.ts diff --git a/tests/baselines/reference/errorInUnnamedClassExpression.errors.txt b/tests/baselines/reference/errorInUnnamedClassExpression.errors.txt new file mode 100644 index 0000000000000..dd20f1b82333c --- /dev/null +++ b/tests/baselines/reference/errorInUnnamedClassExpression.errors.txt @@ -0,0 +1,21 @@ +errorInUnnamedClassExpression.ts(5,14): error TS2715: Abstract property 'bar' in class 'Foo' cannot be accessed in the constructor. +errorInUnnamedClassExpression.ts(7,5): error TS1253: Abstract properties can only appear within an abstract class. +errorInUnnamedClassExpression.ts(7,14): error TS7008: Member 'bar' implicitly has an 'any' type. + + +==== errorInUnnamedClassExpression.ts (3 errors) ==== + // https://github.com/microsoft/TypeScript/issues/62920 + + let Foo = class { + constructor() { + this.bar++; + ~~~ +!!! error TS2715: Abstract property 'bar' in class 'Foo' cannot be accessed in the constructor. + } + abstract bar; + ~~~~~~~~ +!!! error TS1253: Abstract properties can only appear within an abstract class. + ~~~ +!!! error TS7008: Member 'bar' implicitly has an 'any' type. + }; + \ No newline at end of file diff --git a/tests/baselines/reference/errorInUnnamedClassExpression.symbols b/tests/baselines/reference/errorInUnnamedClassExpression.symbols new file mode 100644 index 0000000000000..66f6df763605c --- /dev/null +++ b/tests/baselines/reference/errorInUnnamedClassExpression.symbols @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/errorInUnnamedClassExpression.ts] //// + +=== errorInUnnamedClassExpression.ts === +// https://github.com/microsoft/TypeScript/issues/62920 + +let Foo = class { +>Foo : Symbol(Foo, Decl(errorInUnnamedClassExpression.ts, 2, 3)) + + constructor() { + this.bar++; +>this.bar : Symbol(Foo.bar, Decl(errorInUnnamedClassExpression.ts, 5, 5)) +>this : Symbol(Foo, Decl(errorInUnnamedClassExpression.ts, 2, 9)) +>bar : Symbol(Foo.bar, Decl(errorInUnnamedClassExpression.ts, 5, 5)) + } + abstract bar; +>bar : Symbol(Foo.bar, Decl(errorInUnnamedClassExpression.ts, 5, 5)) + +}; + diff --git a/tests/baselines/reference/errorInUnnamedClassExpression.types b/tests/baselines/reference/errorInUnnamedClassExpression.types new file mode 100644 index 0000000000000..aeb2d6a6c72ef --- /dev/null +++ b/tests/baselines/reference/errorInUnnamedClassExpression.types @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/errorInUnnamedClassExpression.ts] //// + +=== errorInUnnamedClassExpression.ts === +// https://github.com/microsoft/TypeScript/issues/62920 + +let Foo = class { +>Foo : typeof Foo +> : ^^^^^^^^^^ +>class { constructor() { this.bar++; } abstract bar;} : typeof Foo +> : ^^^^^^^^^^ + + constructor() { + this.bar++; +>this.bar++ : number +> : ^^^^^^ +>this.bar : any +> : ^^^ +>this : this +> : ^^^^ +>bar : any +> : ^^^ + } + abstract bar; +>bar : any +> : ^^^ + +}; + diff --git a/tests/cases/compiler/errorInUnnamedClassExpression.ts b/tests/cases/compiler/errorInUnnamedClassExpression.ts new file mode 100644 index 0000000000000..f2070a81e21f4 --- /dev/null +++ b/tests/cases/compiler/errorInUnnamedClassExpression.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62920 + +let Foo = class { + constructor() { + this.bar++; + } + abstract bar; +}; From 28db5fc6dacd10eae8eb7b1d88a95e2a20a7bd68 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 23 Dec 2025 07:20:03 -1000 Subject: [PATCH 3/3] Fix formatting --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e9b915fd64cd7..e011fdf377e8e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34450,7 +34450,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isThisProperty(location) || isThisInitializedObjectBindingExpression(location) || isObjectBindingPattern(location.parent) && isThisInitializedDeclaration(location.parent.parent)) ) { const parentSymbol = getParentOfSymbol(prop); - if (parentSymbol && parentSymbol.flags&SymbolFlags.Class && isNodeUsedDuringClassInitialization(location)) { + if (parentSymbol && parentSymbol.flags & SymbolFlags.Class && isNodeUsedDuringClassInitialization(location)) { if (errorNode) { error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), symbolToString(parentSymbol)); }