fix(eslint-plugin): [no-unnecessary-type-assertion] avoid false positive for template literal expressions#12281
Conversation
…teral false positive
|
Thanks for the PR, @anasm266! 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 on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
View your CI Pipeline Execution ↗ for commit 382f88e
☁️ Nx Cloud last updated this comment at |
kirkwaiblinger
left a comment
There was a problem hiding this comment.
Please follow the AI contribution policy. The PR description is currently overtly in violation.
I'd normally close this PR on sight, but I see you've already successfully followed the AI policy on other PRs, such as #12278... What happened here? In any case please fix this one up for review.
|
|
||
| const isContextuallyUnnecessary = | ||
| !typeAnnotationIsConstAssertion && | ||
| !isTemplateLiteralWithExpressions(node.expression) && |
There was a problem hiding this comment.
No tests fail if I place
if (isTemplateLiteralWithExpressions(node.expression)) {
return;
}right at the top of the 'TSAsExpression, TSTypeAssertion' selector.
Similarly, the isTemplateLiteralWithExpressions(node.expression) is rendered useless by this change:
function isImplicitlyNarrowedLiteralDeclaration({
expression,
parent,
}: TSESTree.TSAsExpression | TSESTree.TSTypeAssertion): boolean {
/**
* Even on `const` variable declarations, template literals with expressions can sometimes be widened without a type assertion.
* @see https://github.com/typescript-eslint/typescript-eslint/issues/8737
*/
if (isTemplateLiteralWithExpressions(expression)) {
return false;
}This indicates one or more of:
- the new check is not placed in the correct position
- the tests are lacking and should give a counterexample to why the new check should be placed where it is, not be at the beginning of the selector
- the existing check is now obsolete and should be cleaned up
Would you please have a look to determine which it is and ensure that the tests support that outcome? thanks!
Also, for context, the added check is a syntactic check (fast), whereas the type analyses are comparatively slow. Therefore, it's beneficial to short-circuit on syntactic analysis before type analyses when possible.
There was a problem hiding this comment.
Thanks, updated in 225116c.
I kept the template-literal check scoped to contextual type fallback rather than returning at the top of the selector. I also added an interpolated-template invalid case that still reports as unnecessaryAssertion, which would fail if the check were moved to the top-level selector.
The existing isImplicitlyNarrowedLiteralDeclaration check remains relevant for the earlier unchanged-type/literal inference path.
There was a problem hiding this comment.
Sorry, the existing template literal check is still not doing anything in any test, though, since the return false; is never executed.
You can see this on the coverage report (or by popping open the debugger or by replacing the return false; with throw new Error() or whatever):
There was a problem hiding this comment.
I added a valid checkLiteralConstAssertions: true case for the #8737 template-literal branch.
Locally, replacing the return false in isImplicitlyNarrowedLiteralDeclaration with throw new Error('hit') now fails on that new test, so that branch is exercised.
I also rechecked the existing interpolated-template as string invalid case: adding a blanket top-level return for interpolated templates makes that case stop reporting, so the #12276 guard still needs to stay scoped to the contextual fallback path.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #12281 +/- ##
==========================================
+ Coverage 86.99% 87.00% +0.01%
==========================================
Files 513 513
Lines 16546 16548 +2
Branches 5165 5166 +1
==========================================
+ Hits 14394 14398 +4
+ Misses 1461 1460 -1
+ Partials 691 690 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
kirkwaiblinger
left a comment
There was a problem hiding this comment.
Please have another look at #12281 (comment) 🙏
|
Ok, I think I'm happy with this. Thank you for for crafting tests to prove out the change! |
565e666
into
typescript-eslint:main
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.61.1 | | npm | @typescript-eslint/parser | 8.61.0 | 8.61.1 | ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.62.0 | | npm | @typescript-eslint/parser | 8.61.0 | 8.62.0 | ## [v8.62.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8620-2026-06-22) ##### 🚀 Features - remove redundant package.json "files" ([#12444](typescript-eslint/typescript-eslint#12444)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.62.0 | | npm | @typescript-eslint/parser | 8.61.0 | 8.62.0 | ## [v8.62.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8620-2026-06-22) ##### 🚀 Features - remove redundant package.json "files" ([#12444](typescript-eslint/typescript-eslint#12444)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.62.0 | | npm | @typescript-eslint/parser | 8.61.0 | 8.62.0 | ## [v8.62.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8620-2026-06-22) ##### 🚀 Features - remove redundant package.json "files" ([#12444](typescript-eslint/typescript-eslint#12444)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.62.1 | | npm | @typescript-eslint/parser | 8.61.0 | 8.62.1 | ## [v8.62.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8621-2026-06-29) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-type-assertion] parenthesize object literal at left edge of expression statement ([#12443](typescript-eslint/typescript-eslint#12443), [#12418](typescript-eslint/typescript-eslint#12418)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] preserve boolean result in fixer for nullable true comparisons ([#12365](typescript-eslint/typescript-eslint#12365)) - **eslint-plugin:** \[prefer-optional-chain] use suggestion instead of autofix for trailing binary operator ([#12328](typescript-eslint/typescript-eslint#12328)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - mdm317 - Patrick Aleite - 송재욱 See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.62.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8620-2026-06-22) ##### 🚀 Features - remove redundant package.json "files" ([#12444](typescript-eslint/typescript-eslint#12444)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.
| datasource | package | from | to | | ---------- | -------------------------------- | ------ | ------ | | npm | @typescript-eslint/eslint-plugin | 8.61.0 | 8.62.1 | | npm | @typescript-eslint/parser | 8.61.0 | 8.62.1 | ## [v8.62.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8621-2026-06-29) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-type-assertion] parenthesize object literal at left edge of expression statement ([#12443](typescript-eslint/typescript-eslint#12443), [#12418](typescript-eslint/typescript-eslint#12418)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] preserve boolean result in fixer for nullable true comparisons ([#12365](typescript-eslint/typescript-eslint#12365)) - **eslint-plugin:** \[prefer-optional-chain] use suggestion instead of autofix for trailing binary operator ([#12328](typescript-eslint/typescript-eslint#12328)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - mdm317 - Patrick Aleite - 송재욱 See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.62.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8620-2026-06-22) ##### 🚀 Features - remove redundant package.json "files" ([#12444](typescript-eslint/typescript-eslint#12444)) ##### ❤️ Thank You - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.62.0) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website. ## [v8.61.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8611-2026-06-15) ##### 🩹 Fixes - **eslint-plugin:** \[no-unnecessary-template-expression] respect ECMAScript line terminators ([#12388](typescript-eslint/typescript-eslint#12388)) - **eslint-plugin:** \[no-unnecessary-boolean-literal-compare] fix precedence bug in autofix ([#12413](typescript-eslint/typescript-eslint#12413)) - **eslint-plugin:** \[no-unnecessary-type-assertion] wrap object literal in parens when removing TSTypeAssertion in arrow body ([#12394](typescript-eslint/typescript-eslint#12394), [#12393](typescript-eslint/typescript-eslint#12393)) - **eslint-plugin:** \[no-unnecessary-type-assertion] avoid false positive for template literal expressions ([#12281](typescript-eslint/typescript-eslint#12281)) - **eslint-plugin:** \[consistent-indexed-object-style] do not remove comments when fixing ([#12396](typescript-eslint/typescript-eslint#12396), [#10577](typescript-eslint/typescript-eslint#10577)) ##### ❤️ Thank You - Anas [@anasm266](https://github.com/anasm266) - Deftera [@Deftera186](https://github.com/Deftera186) - Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger) - lumir - Sarath Francis [@sarathfrancis90](https://github.com/sarathfrancis90) See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.61.1) for more information. You can read about our [versioning strategy](https://typescript-eslint.io/users/versioning) and [releases](https://typescript-eslint.io/users/releases) on our website.

PR Checklist
Overview
Skips the contextual unnecessary-assertion check for template literal expressions with interpolations, where the assertion is still needed for the mapped
ValuePathcase from #12276 to typecheck.Adds a regression test for the issue repro.
💖