-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
base: main
Are you sure you want to change the base?
Conversation
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. |
There was a problem hiding this comment.
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:
- 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. - 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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 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()
There was a problem hiding this comment.
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 😅
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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))
}
}
There was a problem hiding this comment.
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.
cc699ba
to
ef0a755
Compare
[Symbol.asyncIterator]
and [Symbol.iterator]
methods as used in iterationsThere was a problem hiding this 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?
src/compiler/checker.ts
Outdated
isIdentifier(node.expression) && | ||
node.expression.escapedText === "Symbol" | ||
)) { | ||
return false; |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is widened to an ordinary symbol.
There was a problem hiding this comment.
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 symbol
s nowadays.
There was a problem hiding this 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 symbol
s 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)
Fixes #42051