Skip to content

allow unused private well-known symbol members #42104

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 3 commits into
base: main
Choose a base branch
from

Conversation

Zzzen
Copy link
Contributor

@Zzzen Zzzen commented Dec 24, 2020

Fixes #42051

Comment on lines 33 to 38
private async *[Symbol.asyncIterator]() {}
~~~~~~~~~~~~~~~~~~~~~~
!!! error TS6133: '[Symbol.asyncIterator]' is declared but its value is never read.
private *[Symbol.iterator]() {}
~~~~~~~~~~~~~~~~~
!!! error TS6133: '[Symbol.iterator]' is declared but its value is never read.
Copy link
Member

Choose a reason for hiding this comment

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

It’s a little confusing to consider what it means for these members to be marked as private, but let’s think about the consequences:

  1. Within the program, consumers can spread or for/of an instance of Unused with no problems, but they can’t directly access this member via element access.
  2. Outside the program (i.e., consumers of the declaration emit as a library) can spread or for/of an instance of Unused, but they don’t know what type it yields. (The member declaration is preserved, but the type annotation is erased.)

I think because these members are to some degree usable by external consumers, we can’t ever mark them unused (at least, not if the class is exported or globally accessible).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. Outside the program (i.e., consumers of the declaration emit as a library) can spread or for/of an instance of Unused, but they don’t know what type it yields. (The member declaration is preserved, but the type annotation is erased.)

🤔 Shouldn't symbol members behave like ordinary members? I don't know why not all following accesses report errors? Playground

class UsedOutsideClass {
    private *[Symbol.iterator](){ return ''; }
    private ['bar'] = () => {}
    private baz = () => {}
}

const foo = new UsedOutsideClass();
// No error ❓
foo[Symbol.iterator]()
// No error ❓
foo['bar']()
// Error ✅
foo.baz()

Copy link
Member

Choose a reason for hiding this comment

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

Uhhh, that looks like a separate bug to me 😅

Copy link
Member

Choose a reason for hiding this comment

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

The salient thing here isn’t the member declaration, it’s the access. foo.bar is an error but foo['bar'] is not. Anyway, don’t worry about that for this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😕 So we should simply ignore private Symbol.iterator/Symbol.asyncIterator when checking unused members?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, at least when the class is global or exported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It occurs to me that maybe all well-known symbols should be ignored because they are designed to work implicitly and we have no practical way to track the usages.

class Foo {
  // Error: '[Symbol.toPrimitive]' is declared but its value is never read.
  private [Symbol.toPrimitive]() {
    return 1;
  }

  test() {
    // Logs 1
    console.log(String(this))
  }
}

Copy link
Member

Choose a reason for hiding this comment

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

That seems plausible to me.

@Zzzen Zzzen force-pushed the mark-itertors-symbols-as-used branch from cc699ba to ef0a755 Compare April 23, 2021 16:32
@Zzzen Zzzen changed the title mark [Symbol.asyncIterator] and [Symbol.iterator] methods as used in iterations allow unused private well-known symbol members Apr 23, 2021
@sandersn sandersn assigned andrewbranch and unassigned armanio123 Mar 16, 2022
andrewbranch
andrewbranch previously approved these changes Mar 16, 2022
Copy link
Member

@andrewbranch andrewbranch left a comment

Choose a reason for hiding this comment

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

This seems good to me. I’d like one more person to weigh in. @rbuckton @weswigham?

@andrewbranch andrewbranch requested review from rbuckton and weswigham and removed request for elibarzilay March 17, 2022 22:59
isIdentifier(node.expression) &&
node.expression.escapedText === "Symbol"
)) {
return false;
Copy link
Member

Choose a reason for hiding this comment

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

I don't like this. We're able to do way better than syntactic specialization. Instead, check if the type of the name expression is the same as one of the members of the global SymbolConstructor interface.

export class C {
private *[iteratorSymbol]() {
~~~~~~~~~~~~~~~~
!!! error TS6133: '[iteratorSymbol]' is declared but its value is never read.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We probably should not error here, but It is gonna take a lot of effort to fix.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, this should be easy to fix. Compare the type of the computer name, rather than the symbol. Every unique symbol has a unique type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Hm, definitely a bug - I've opened #53276 to track it, since all members of the global SymbolConstructor are, in fact, declared as unique symbols nowadays.

Copy link
Member

@weswigham weswigham left a comment

Choose a reason for hiding this comment

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

I think this is close to the right approach, with the changes I suggested, but is blocked on #53276 being fixed. I'd prefer we didn't have anything special for well-known symbols that doesn't apply to unique symbols in general, but relaxing usage checks for them, since they specify language-level protocol implementations, even when private, is a reasonable exception to the rule. (Though I could be wrong about that - someone may be equally sad that their private [myProtocolSymbol] method is marked as unused; it's just less common)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
Status: Waiting on author
Development

Successfully merging this pull request may close these issues.

Private [Symbol.asyncIterator] and [Symbol.iterator] methods incorrectly reported as unused
5 participants