Skip to content

Unify the way anonymous class symbols are printed in error messages #61943

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
8 changes: 3 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24237,13 +24237,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const privateIdentifierDescription = unmatchedProperty.valueDeclaration.name.escapedText;
const symbolTableKey = getSymbolNameForPrivateIdentifier(source.symbol, privateIdentifierDescription);
if (symbolTableKey && getPropertyOfType(source, symbolTableKey)) {
const sourceName = factory.getDeclarationName(source.symbol.valueDeclaration);
const targetName = factory.getDeclarationName(target.symbol.valueDeclaration);
reportError(
Diagnostics.Property_0_in_type_1_refers_to_a_different_member_that_cannot_be_accessed_from_within_type_2,
diagnosticName(privateIdentifierDescription),
diagnosticName(sourceName.escapedText === "" ? anon : sourceName),
diagnosticName(targetName.escapedText === "" ? anon : targetName),
symbolToString(source.symbol),
symbolToString(target.symbol),
);
return;
}
Expand Down Expand Up @@ -34681,7 +34679,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
right,
Diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier,
diagName,
diagnosticName(typeClass.name || anon),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think anon can now be completely removed. Only checkNoTypeArguments relies on it - but I can't see how it would actually end up in that branch (and there are no tests proving otherwise). I have refrained myself from doing changes there though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this proposed patch makes it a little bit more clear why I think this anon branch there is essentially never hit:

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 7716a39688..ef89daf9b9 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1141,7 +1141,6 @@ import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js";
 import * as performance from "./_namespaces/ts.performance.js";
 
 const ambientModuleSymbolRegex = /^".+"$/;
-const anon = "(anonymous)" as __String & string;
 
 const enum ReferenceHint {
     Unspecified,
@@ -17161,9 +17160,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
         return !!(node.flags & NodeFlags.JSDoc) && (node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ImportType);
     }
 
-    function checkNoTypeArguments(node: NodeWithTypeArguments, symbol?: Symbol) {
+    function checkNoTypeArguments(node: NodeWithTypeArguments, symbol: Symbol): boolean 
+    function checkNoTypeArguments(node: TypeReferenceNode): boolean 
+    function checkNoTypeArguments(node: TypeReferenceNode | NodeWithTypeArguments, symbol?: Symbol) {
         if (node.typeArguments) {
-            error(node, Diagnostics.Type_0_is_not_generic, symbol ? symbolToString(symbol) : (node as TypeReferenceNode).typeName ? declarationNameToString((node as TypeReferenceNode).typeName) : anon);
+            let name
+            if (isTypeReferenceNode(node)) {
+                name = declarationNameToString(node.typeName);
+            } else {
+                Debug.assertIsDefined(symbol);
+                name = symbolToString(symbol);
+            }
+            error(node, Diagnostics.Type_0_is_not_generic, name);
             return false;
         }
         return true;

This function is always called with a symbol when the argument is NodeWithTypeArguments and it's never called with a symbol when the argument is TypeReferenceNode. And TypeReferenceNode always has a typeName property.

symbolToString(typeClass.symbol),
);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
privateNameMethodClassExpression.ts(9,17): error TS18013: Property '#method' is not accessible outside class '(anonymous)' because it has a private identifier.
privateNameMethodClassExpression.ts(10,17): error TS18013: Property '#field' is not accessible outside class '(anonymous)' because it has a private identifier.
privateNameMethodClassExpression.ts(9,17): error TS18013: Property '#method' is not accessible outside class 'C' because it has a private identifier.
privateNameMethodClassExpression.ts(10,17): error TS18013: Property '#field' is not accessible outside class 'C' because it has a private identifier.


==== privateNameMethodClassExpression.ts (2 errors) ====
Expand All @@ -13,9 +13,9 @@ privateNameMethodClassExpression.ts(10,17): error TS18013: Property '#field' is
console.log(C.getInstance().getField());
C.getInstance().#method; // Error
~~~~~~~
!!! error TS18013: Property '#method' is not accessible outside class '(anonymous)' because it has a private identifier.
!!! error TS18013: Property '#method' is not accessible outside class 'C' because it has a private identifier.
C.getInstance().#field; // Error
~~~~~~
!!! error TS18013: Property '#field' is not accessible outside class '(anonymous)' because it has a private identifier.
!!! error TS18013: Property '#field' is not accessible outside class 'C' because it has a private identifier.


Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
privateNameMethodClassExpression2.ts(5,6): error TS18013: Property '#method' is not accessible outside class '(Anonymous class)' because it has a private identifier.
privateNameMethodClassExpression2.ts(9,6): error TS18013: Property '#field' is not accessible outside class '(Anonymous class)' because it has a private identifier.


==== privateNameMethodClassExpression2.ts (2 errors) ====
new (class {
#method() {
return 42;
}
})().#method; // error
~~~~~~~
!!! error TS18013: Property '#method' is not accessible outside class '(Anonymous class)' because it has a private identifier.

new (class {
#field = 42;
})().#field; // error
~~~~~~
!!! error TS18013: Property '#field' is not accessible outside class '(Anonymous class)' because it has a private identifier.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [tests/cases/compiler/privateNameMethodClassExpression2.ts] ////

=== privateNameMethodClassExpression2.ts ===
new (class {
#method() {
>#method : Symbol((Anonymous class).#method, Decl(privateNameMethodClassExpression2.ts, 0, 12))

return 42;
}
})().#method; // error

new (class {
#field = 42;
>#field : Symbol((Anonymous class).#field, Decl(privateNameMethodClassExpression2.ts, 6, 12))

})().#field; // error

41 changes: 41 additions & 0 deletions tests/baselines/reference/privateNameMethodClassExpression2.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//// [tests/cases/compiler/privateNameMethodClassExpression2.ts] ////

=== privateNameMethodClassExpression2.ts ===
new (class {
>new (class { #method() { return 42; }})().#method : any
> : ^^^
>new (class { #method() { return 42; }})() : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>(class { #method() { return 42; }}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>class { #method() { return 42; }} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

#method() {
>#method : () => number
> : ^^^^^^^^^^^^

return 42;
>42 : 42
> : ^^
}
})().#method; // error

new (class {
>new (class { #field = 42;})().#field : any
> : ^^^
>new (class { #field = 42;})() : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>(class { #field = 42;}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>class { #field = 42;} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

#field = 42;
>#field : number
> : ^^^^^^
>42 : 42
> : ^^

})().#field; // error

17 changes: 17 additions & 0 deletions tests/baselines/reference/privateNamesUnique-6.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
privateNamesUnique-6.ts(6,7): error TS2322: Type '(Anonymous class)' is not assignable to type 'A'.
Property '#foo' in type '(Anonymous class)' refers to a different member that cannot be accessed from within type 'A'.


==== privateNamesUnique-6.ts (1 errors) ====
class A {
#foo: number;
}

// error
const test: A = new (class {
~~~~
!!! error TS2322: Type '(Anonymous class)' is not assignable to type 'A'.
!!! error TS2322: Property '#foo' in type '(Anonymous class)' refers to a different member that cannot be accessed from within type 'A'.
#foo: number;
})();

20 changes: 20 additions & 0 deletions tests/baselines/reference/privateNamesUnique-6.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//// [tests/cases/compiler/privateNamesUnique-6.ts] ////

=== privateNamesUnique-6.ts ===
class A {
>A : Symbol(A, Decl(privateNamesUnique-6.ts, 0, 0))

#foo: number;
>#foo : Symbol(A.#foo, Decl(privateNamesUnique-6.ts, 0, 9))
}

// error
const test: A = new (class {
>test : Symbol(test, Decl(privateNamesUnique-6.ts, 5, 5))
>A : Symbol(A, Decl(privateNamesUnique-6.ts, 0, 0))

#foo: number;
>#foo : Symbol((Anonymous class).#foo, Decl(privateNamesUnique-6.ts, 5, 28))

})();

29 changes: 29 additions & 0 deletions tests/baselines/reference/privateNamesUnique-6.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [tests/cases/compiler/privateNamesUnique-6.ts] ////

=== privateNamesUnique-6.ts ===
class A {
>A : A
> : ^

#foo: number;
>#foo : number
> : ^^^^^^
}

// error
const test: A = new (class {
>test : A
> : ^
>new (class { #foo: number;})() : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>(class { #foo: number;}) : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>class { #foo: number;} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^

#foo: number;
>#foo : number
> : ^^^^^^

})();

13 changes: 13 additions & 0 deletions tests/cases/compiler/privateNameMethodClassExpression2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @strict: true
// @target: es2015
// @noEmit: true

new (class {
#method() {
return 42;
}
})().#method; // error

new (class {
#field = 42;
})().#field; // error
13 changes: 13 additions & 0 deletions tests/cases/compiler/privateNamesUnique-6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @strict: true
// @strictPropertyInitialization: false
// @target: es6
// @noEmit: true

class A {
#foo: number;
}

// error
const test: A = new (class {
#foo: number;
})();