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-unsafe-type-assertion] fix for unsafe assertion to a constrained type parameter#10461
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
Uh oh!
There was an error while loading.Please reload this page.
Conversation
Thanks for the PR,@controversial! 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 commentedDec 5, 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 commentedDec 5, 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.
codecovbot commentedDec 5, 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 #10461 +/- ##======================================= Coverage 86.78% 86.79% ======================================= Files 445 445 Lines 15366 15376 +10 Branches 4475 4478 +3 =======================================+ Hits 13336 13346 +10 Misses 1675 1675 Partials 355 355
Flags with carried forward coverage won't be shown.Click here to find out more.
|
controversial commentedDec 5, 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.
With respect to a custom error message,@kirkwaiblingersuggested:
In the original PR of this rule,#10051, a discussion about the appropriate verbosity of the error messageresolved to omit any types other than theexplicitly written type from the error message in order to prevent excessively verbose errors. With that in mind, I’m starting out with the following error message:
I’m happy to amend this message to be more verbose if we decide to go that way, 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.
What a great first PR to typescript-eslint! This is very much on the right track, and in a tricky area of the codebase, too.
Left a few comments and questions.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
checker.getBaseConstraintOfType(assertedType); | ||
const isAssignableToConstraint = | ||
!!assertedTypeConstraint && | ||
checker.isTypeAssignableTo( |
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 specific changes requested in the following)
While I was thinking deeply, it occurred to make that I don't think that asserting from any concrete type to any type parameter is ever safe, so I was wondering whether we could gain some performance by checking for this early and avoiding anychecker.isTypeAssignableTo()
checks..
This would sacrifice the ability to have distinct error messages for whether it's not assignable because of ordinary unassignability (string | number as T where T extends string) or due to the generic subtype instantiation (string as T where T extends string | number), it could potentially be mitigated by a separate error message just saying A concrete type can never be safely assigned to a type parameter (or similar).
However, I think we still need the current infrastructure in order to deal with the case of assignment from one type parameter to another type parameter (since T -> V where V extends T is ok), so I'm leaning against going that route, i.e. leaning towards your current strategy. If you want to give this a thought, though, I'm curious for your opinion as well!
…ssage+ light refactor to prefer early returns over nested conditionals
…r extends unconstrained parameter”
controversial commentedDec 6, 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.
Thanks so much for the thoughtful review@kirkwaiblinger !! I added the test cases from your playground and addressed the missing cases. For the missing case ofunconstrained type parameters, I added another message to match the one Typescript uses:
Here’s an updatedplayground link! The only test case that tripped me up a bit was the one you had called |
Merged the latest When you get a chance, let me know if there’s anything else you need from me@kirkwaiblinger :) |
I feel strongly that TS is wrong here, since
🎉 |
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.
Really outstanding work! Great attention to detail on noticing the additional error message, nicely done on pushing back where it made sense to do so!
Trivial cleanup requested but this is otherwise an enthusiastic approval from me! 🙂 We'll probably leave this open for a little bit as well for another team member to take a look if they like, since it's nuanced stuff.
Thanks!
packages/eslint-plugin/tests/rules/no-unsafe-type-assertion.test.ts OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
packages/eslint-plugin/tests/rules/no-unsafe-type-assertion.test.ts OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
unit tests should only test one thing
cleanup completed! thanks again for your thoughtful reviews, excited to have this merged at some point :) |
Uh oh!
There was an error while loading.Please reload this page.
4c91ed5
intotypescript-eslint:mainUh oh!
There was an error while loading.Please reload this page.
| datasource | package | from | to || ---------- | -------------------------------- | ------ | ------ || npm | @typescript-eslint/eslint-plugin | 8.18.1 | 8.18.2 || npm | @typescript-eslint/parser | 8.18.1 | 8.18.2 |## [v8.18.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8182-2024-12-23)##### 🩹 Fixes- **eslint-plugin:** \[no-unnecessary-condition] handle noUncheckedIndexedAccess true ([#10514](typescript-eslint/typescript-eslint#10514))- **eslint-plugin:** \[consistent-type-assertions] allow default assertionStyle option ([#10512](typescript-eslint/typescript-eslint#10512))- **eslint-plugin:** \[no-unnecessary-type-arguments] handle type/value context ([#10503](typescript-eslint/typescript-eslint#10503))- **eslint-plugin:** \[no-unsafe-type-assertion] fix for unsafe assertion to a constrained type parameter ([#10461](typescript-eslint/typescript-eslint#10461))- **eslint-plugin:** \[consistent-indexed-object-style] use a suggestion over an auto-fix if can't reliably determine that produced index signature is valid ([#10490](typescript-eslint/typescript-eslint#10490))- **eslint-plugin:** \[no-unnecessary-condition] don't flag values of an unconstrained or valid type parameter ([#10473](typescript-eslint/typescript-eslint#10473))- **eslint-plugin:** \[prefer-reduce-type-parameter] don't report cases in which the fix results in a type error ([#10494](typescript-eslint/typescript-eslint#10494))- **eslint-plugin:** \[no-deprecated] not reporting usages of deprecated declared constants as object value ([#10498](typescript-eslint/typescript-eslint#10498))##### ❤️ Thank You- Luke Deen Taylor [@controversial](https://github.com/controversial)- Ronen Amiel- Scott O'Hara- YeonJuan [@yeonjuan](https://github.com/yeonjuan)- Yukihiro Hasegawa [@y-hsgw](https://github.com/y-hsgw)You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
##### [v8.18.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8182-2024-12-23)##### 🩹 Fixes- **eslint-plugin:** \[no-unnecessary-condition] handle noUncheckedIndexedAccess true ([#10514](typescript-eslint/typescript-eslint#10514))- **eslint-plugin:** \[consistent-type-assertions] allow default assertionStyle option ([#10512](typescript-eslint/typescript-eslint#10512))- **eslint-plugin:** \[no-unnecessary-type-arguments] handle type/value context ([#10503](typescript-eslint/typescript-eslint#10503))- **eslint-plugin:** \[no-unsafe-type-assertion] fix for unsafe assertion to a constrained type parameter ([#10461](typescript-eslint/typescript-eslint#10461))- **eslint-plugin:** \[consistent-indexed-object-style] use a suggestion over an auto-fix if can't reliably determine that produced index signature is valid ([#10490](typescript-eslint/typescript-eslint#10490))- **eslint-plugin:** \[no-unnecessary-condition] don't flag values of an unconstrained or valid type parameter ([#10473](typescript-eslint/typescript-eslint#10473))- **eslint-plugin:** \[prefer-reduce-type-parameter] don't report cases in which the fix results in a type error ([#10494](typescript-eslint/typescript-eslint#10494))- **eslint-plugin:** \[no-deprecated] not reporting usages of deprecated declared constants as object value ([#10498](typescript-eslint/typescript-eslint#10498))##### ❤️ Thank You- Luke Deen Taylor [@controversial](https://github.com/controversial)- Ronen Amiel- Scott O'Hara- YeonJuan [@yeonjuan](https://github.com/yeonjuan)- Yukihiro Hasegawa [@y-hsgw](https://github.com/y-hsgw)You can read about our [versioning strategy](https://main--typescript-eslint.netlify.app/users/versioning) and [releases](https://main--typescript-eslint.netlify.app/users/releases) on our website.
Uh oh!
There was an error while loading.Please reload this page.
PR Checklist
Overview
Attempts to fix the issue described in#10453:
After experimenting with the logic in
no-unsafe-type-assertion
, I found that obtaining the “types to check” usingservices.getTypeAtLocation
in place ofgetConstrainedTypeAtLocation
(i.e. omitting a call togetBaseConstraintOfType
) allows the subsequentisTypeAssignableTo
check to correctly catch this case.Additionally, I added a new error message for this case: if the “expression type” isnot assignable to the “asserted type”, but the asserted type has a constraint to which the expression type is assignable, the rule will report the following:
This is my first contribution to this project and I’m broadly unfamiliar with both ESLint plugin development and TypeScript internals, so it’s very possible that I’m missing a hidden implication of this change—i.e. I’m not sure if there was an important reason to use
getConstrainedTypeAtLocation
in the original implementation. However, the existing tests for this rule are still passing.