allow unused private well-known symbol members#42104
allow unused private well-known symbol members#42104Zzzen wants to merge 3 commits intomicrosoft:mainfrom
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.
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
Unusedwith 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.
- 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.
Uhhh, that looks like a separate bug to me 😅
There was a problem hiding this comment.
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.
😕 So we should simply ignore private Symbol.iterator/Symbol.asyncIterator when checking unused members?
There was a problem hiding this comment.
Yes, at least when the class is global or exported.
There was a problem hiding this comment.
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.
That seems plausible to me.
cc699ba to
ef0a755
Compare
[Symbol.asyncIterator] and [Symbol.iterator] methods as used in iterations
andrewbranch
left a comment
There was a problem hiding this comment.
This seems good to me. I’d like one more person to weigh in. @rbuckton @weswigham?
| isIdentifier(node.expression) && | ||
| node.expression.escapedText === "Symbol" | ||
| )) { | ||
| return false; |
There was a problem hiding this comment.
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.
We probably should not error here, but It is gonna take a lot of effort to fix.
There was a problem hiding this comment.
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.
No, it is widened to an ordinary symbol.
There was a problem hiding this comment.
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.
weswigham
left a comment
There was a problem hiding this comment.
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)


Fixes #42051