Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork2.8k
fix(eslint-plugin): [no-misused-promises] theinheritedMethods
andproperties
options to check all statically analyzable declarations#10310
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 ourterms of service andprivacy 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
Thanks for the PR,@ronami! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently onhttps://opencollective.com/typescript-eslint. |
netlifybot commentedNov 8, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
✅ Deploy Preview fortypescript-eslint ready!
To edit notification comments on pull requests, go to yourNetlify site configuration. |
nx-cloudbot commentedNov 8, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
View yourCI Pipeline Execution ↗ for commit6c48db9.
☁️Nx Cloud last updated this comment at |
inheritedMethods
andproperties
to check all statically analyzable declarationsinheritedMethods
andproperties
options to check all statically analyzable declarationsfunction getWellKnownStringOfSymbol(symbol: symbol): string | null { | ||
switch (symbol) { | ||
case Symbol.iterator: | ||
return 'iterator'; | ||
case Symbol.asyncIterator: | ||
return 'asyncIterator'; | ||
} | ||
return null; | ||
} |
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'm not sure if there's a better way to map a symbol to its string counterparttsutils.getWellKnownSymbolPropertyOfType
is expecting.
The list forwell-known symbols isn't long, but this will still require maintaining this mapping.
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.
If this approach makes sense, this list can be expanded to support other well-known symbols.
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.
Is there a reason we need to stick to well-known symbols? I'd think we should go with any detected symbol, no?
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.
Hmm, I might be missing something, but I'm not entirely sure how to check if a JavaScriptsymbol
exists on anotherts.Type
🤔
Here's how existing code checks if an inherited type has astring
symbol:
constescapedMemberName=ts.escapeLeadingUnderscores(staticAccessValue);constsymbolMemberMatch=type.getSymbol()?.members?.get(escapedMemberName);return(symbolMemberMatch??tsutils.getPropertyOfType(type,escapedMemberName));
I've looked into theSymbolObject
but couldn't find a way to check against an arbitrarysymbol
(not to be confused withts.Symbol
).
Because of this, I've created a function to map between known symbols and their string representation, which are passed totsutils.getWellKnownSymbolPropertyOfType
to check if they exist on the inherited type.
Edit: I don't think it's feasible to iterate the inherited parent's nodes, as a function may be higher up the inheritance chain:
conststaticSymbol=Symbol.for('static symbol');interfaceBar{[staticSymbol]:()=>void;}interfaceFooextendsBar{}classClazzimplementsFoo{// should be reportedasync[staticSymbol](){}}
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.
Hmm gotcha thanks. cc@kirkwaiblinger - I'm fine with this as a patchwork helper, with as little hardcoding as possible.
functiongetWellKnownStringOfSymbol(symbol:symbol):string|null{for(const[key,value]ofObject.entries(Symbol)){if(symbol===value){returnkey;}}returnnull;}
And, sorry for dropping the ball on this again 🤦 there's something about symbols that makes me instinctively forget the conversation...
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've updated the PR to not hard-code well-known symbols, thanks!
The only alternative I had in mind for not hard-coding was to extract the value fromSymbol.prototype.toString()
with a regex (which I really didn't want to do), but I didn't think of iterating the globalSymbol
's keys.
This means that arbitrary symbols (e.g.Symbol.for('foo')
) won't be reported by the rule though.
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.
Yeah I don't see a way to work for arbitrary symbols 😞
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.
cross-linking discord threadhttps://discord.com/channels/1026804805894672454/1322853884292497418
codecovbot commentedNov 8, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@## main #10310 +/- ##==========================================+ Coverage 86.86% 86.92% +0.06%========================================== Files 445 446 +1 Lines 15455 15514 +59 Branches 4507 4520 +13 ==========================================+ Hits 13425 13486 +61+ Misses 1675 1673 -2 Partials 355 355
Flags with carried forward coverage won't be shown.Click here to find out more.
|
node.type !== AST_NODE_TYPES.MethodDefinition && | ||
node.type !== AST_NODE_TYPES.TSAbstractMethodDefinition && | ||
node.type !== AST_NODE_TYPES.TSMethodSignature && | ||
node.type !== AST_NODE_TYPES.TSPropertySignature && | ||
node.type !== AST_NODE_TYPES.PropertyDefinition |
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.
This is enough to pass the test suit but there are additional types available to satisfygetStaticMemberAccessValue
.
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.
This is necessary mainly for type-checking, and I don't know if this can realistically have a runtime scenario. Please let me know if this should be handled differently (currently, there is no coverage for 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.
I don't believe this can realistically be a runtime scenario. I think the real issue here is that althoughnodeMember
is typets.TypeElement | ts.ClassElement
,node
is typeTSESTree.Node
.
The two strategies I'd think could be good would be:
- Simpler: use an
as
to correct TypeScript's understanding ofnode
's type to beNodeWithKey
(which will need to be exported frommisc.ts
) - More aligned with how most of our typed lint rules go: instead of iterating over the
tsNode.members
, iterate over the members ofnode
The latter is probably a lot more work and changes the existing code. So I think theas
strategy would be reasonable.
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, I've started with a type-assertion on my initial implementation but I wasn't sure if there's a realistic case where this would explode.
I updated the code to use a type assertion, as the second option seems to be a lot more work.
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 like where this is going!
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.
[Testing] One thing under test per case please (https://typescript-eslint.io/contributing/pull-requests#granular-unit-tests)
node.type !== AST_NODE_TYPES.MethodDefinition && | ||
node.type !== AST_NODE_TYPES.TSAbstractMethodDefinition && | ||
node.type !== AST_NODE_TYPES.TSMethodSignature && | ||
node.type !== AST_NODE_TYPES.TSPropertySignature && | ||
node.type !== AST_NODE_TYPES.PropertyDefinition |
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 believe this can realistically be a runtime scenario. I think the real issue here is that althoughnodeMember
is typets.TypeElement | ts.ClassElement
,node
is typeTSESTree.Node
.
The two strategies I'd think could be good would be:
- Simpler: use an
as
to correct TypeScript's understanding ofnode
's type to beNodeWithKey
(which will need to be exported frommisc.ts
) - More aligned with how most of our typed lint rules go: instead of iterating over the
tsNode.members
, iterate over the members ofnode
The latter is probably a lot more work and changes the existing code. So I think theas
strategy would be reasonable.
function getWellKnownStringOfSymbol(symbol: symbol): string | null { | ||
switch (symbol) { | ||
case Symbol.iterator: | ||
return 'iterator'; | ||
case Symbol.asyncIterator: | ||
return 'asyncIterator'; | ||
} | ||
return null; | ||
} |
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.
Is there a reason we need to stick to well-known symbols? I'd think we should go with any detected symbol, no?
👋 Hey@ronami, did you want to re-request review on this? No pressure, just asking because it's been a bit and I've seen other things from you in the meantime. 🙂 |
ronami commentedDec 2, 2024 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Hey@JoshuaKGoldberg, thanks for checking up on this ❤️ I'vecommented here on something I'm not sure how to implement (I might be missing something 🙈). I didn't want to ask for a re-review since it's still an open discussion. This is the only remaining open issue before the PR can be re-reviewed. |
context, | ||
); | ||
if (!staticAccessValue) { |
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.
(same as other comment)
if(!staticAccessValue){ | |
if(staticAccessValue==null){ |
@@ -492,9 +492,14 @@ export default createRule<Options, MessageId>({ | |||
if (objType == null) { | |||
return; | |||
} | |||
const propertySymbol = checker.getPropertyOfType( | |||
const staticAccessValue = getStaticMemberAccessValue(node, context); | |||
if (!staticAccessValue) { |
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.
(bug)
if(!staticAccessValue){ | |
if(staticAccessValue==null){ |
because
classMyClass{''():void{}}classMySubclassextendsMyClass{async''():Promise<void>{}}
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.
Oh, nice catch! Is there any reason we don't havestrict-boolean-expressions
enabled?
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.
Because IIRC both Joshs don't like it hehe. Brad and I both do like it. 🤷♂️
function getWellKnownStringOfSymbol(symbol: symbol): string | null { | ||
switch (symbol) { | ||
case Symbol.iterator: | ||
return 'iterator'; | ||
case Symbol.asyncIterator: | ||
return 'asyncIterator'; | ||
} | ||
return null; | ||
} |
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.
cross-linking discord threadhttps://discord.com/channels/1026804805894672454/1322853884292497418
Hey@ronami, do you plan to make further changes regarding#10310 (comment) or is this intended to be reviewed as is? |
ronami commentedJan 19, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Oh, I'm sorry; I've looked into it but completely forgot to get back to it. I didn't consider the issue with the current approach, which could result in different values on different machines and NodeJS versions (though looking intoMDN, it seems pretty much all well-known symbols are supported on rather old Node versions already). I haven't noticed Having a way to get the key type of a property Thanks for your help with this ❤️ |
Ok, that definitely alleviates some of the concern 👍 Though I guess there's maybe still a forwards-compatibility question.
What I usually do is just ask questions on the TS discord and copy down the link on the issue/PR I'm working on - partly for visibility and partly just so I can find it again myself! 😅
❤️ |
👋@ronami just checking in, how is this going? |
Uh oh!
There was an error while loading.Please reload this page.
PR Checklist
inheritedMethods
check should flag all statically analyzable declarations, not just identifiers and numbers. #10211Overview
This PRresolves#10211 and uses the existing
getStaticMemberAccessValue()
utility instead oftsutils.getPropertyOfType()
to handle most statically analyzable declarations/properties:I added some PR review comments on parts of the PR I'm not sure are correct, please let me know what you think.