Skip to content

fix(eslint-plugin): [no-unnecessary-condition] use assignability checks in checkTypePredicates#12147

Merged
bradzacher merged 10 commits intotypescript-eslint:mainfrom
zaewc:fix/no-unnecessary-condition-subtype-check
Apr 12, 2026
Merged

fix(eslint-plugin): [no-unnecessary-condition] use assignability checks in checkTypePredicates#12147
bradzacher merged 10 commits intotypescript-eslint:mainfrom
zaewc:fix/no-unnecessary-condition-subtype-check

Conversation

@zaewc
Copy link
Copy Markdown
Contributor

@zaewc zaewc commented Mar 20, 2026

PR Checklist

Overview

checkTypePredicates previously only flagged a type guard call as unnecessary when the argument's
type was the exact same TS type object as the predicate's asserted type. This meant cases
where the argument is a strict subtype of the asserted type were silently passed over.

e.g, isStringOrNumber(s) where s: string was not flagged, even though string is always
assignable to string | number, making the check redundant.

This pr extends the check to also report when the argument type is a strict subtype of the asserted
type — i.e., assignable one-way but not mutually assignable. Mutual assignability is explicitly
excluded to avoid false positives with structurally compatible types that still produce meaningful
narrowing.

@typescript-eslint
Copy link
Copy Markdown
Contributor

Thanks for the PR, @976520!

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.

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 20, 2026

Deploy Preview for typescript-eslint ready!

Name Link
🔨 Latest commit 1496319
🔍 Latest deploy log https://app.netlify.com/projects/typescript-eslint/deploys/69d7327edf739a0008a6fd7c
😎 Deploy Preview https://deploy-preview-12147--typescript-eslint.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 99 (no change from production)
Accessibility: 97 (no change from production)
Best Practices: 92 (no change from production)
SEO: 90 (no change from production)
PWA: 80 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 20, 2026

View your CI Pipeline Execution ↗ for commit 1496319

Command Status Duration Result
nx run-many -t lint ✅ Succeeded 2m 6s View ↗
nx run types:build ✅ Succeeded 1s View ↗
nx run-many -t typecheck ✅ Succeeded 44s View ↗
nx test typescript-estree --coverage=false ✅ Succeeded 13s View ↗
nx test eslint-plugin-internal --coverage=false ✅ Succeeded 8s View ↗
nx run generate-configs ✅ Succeeded 7s View ↗
nx run integration-tests:test ✅ Succeeded 3s View ↗
nx run-many --target=build --parallel --exclude... ✅ Succeeded 16s View ↗
Additional runs (34) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2026-04-09 05:07:06 UTC

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.71%. Comparing base (b3315fd) to head (1496319).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #12147   +/-   ##
=======================================
  Coverage   86.71%   86.71%           
=======================================
  Files         513      513           
  Lines       16270    16271    +1     
  Branches     5059     5060    +1     
=======================================
+ Hits        14108    14109    +1     
  Misses       1474     1474           
  Partials      688      688           
Flag Coverage Δ
unittest 86.71% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
.../eslint-plugin/src/rules/class-methods-use-this.ts 97.87% <ø> (ø)
...slint-plugin/src/rules/no-unnecessary-condition.ts 97.98% <100.00%> (+<0.01%) ⬆️
...plugin/src/rules/no-unnecessary-type-parameters.ts 92.81% <ø> (ø)
...-plugin/src/rules/no-useless-default-assignment.ts 91.54% <ø> (ø)
packages/typescript-estree/src/check-modifiers.ts 10.47% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zaewc zaewc force-pushed the fix/no-unnecessary-condition-subtype-check branch from 14c521b to fb9a6d4 Compare March 21, 2026 04:43
@bradzacher bradzacher changed the title fix(eslint-plugin): use assignability checks in checkTypePredicates fix(eslint-plugin): [no-unnecessary-condition] use assignability checks in checkTypePredicates Apr 8, 2026
@bradzacher bradzacher changed the title fix(eslint-plugin): [no-unnecessary-condition] use assignability checks in checkTypePredicates fix(eslint-plugin): [no-unnecessary-condition] use assignability checks in checkTypePredicates Apr 8, 2026
Copy link
Copy Markdown
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good so far

interface Wider {
  a: string;
}
interface Narrower {
  a: string;
  b?: number;
}
declare function isWider(x: unknown): x is Wider;
declare const n: Narrower;
if (isWider(n)) {
}

Could you add this as a test case please.
It should error IIUC

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Apr 8, 2026
@zaewc
Copy link
Copy Markdown
Contributor Author

zaewc commented Apr 8, 2026

@bradzacher

The rule wasn't catching this because it compared types with ===, so structurally compatible but distinct interfaces were missed. Fixed by switching to checker.isTypeAssignableTo(). This also uncovered that string literal -> string type guard cases (e.g. isString('falafel')) were incorrectly allowed, moved those to invalid as well.

zaewc added 2 commits April 8, 2026 19:45
Simplify the type guard check to use `checker.isTypeAssignableTo()`
directly instead of restricting to union types. This catches cases
where the argument type is structurally assignable to the predicate
type (e.g. interfaces with optional properties, string literals).
@zaewc zaewc requested a review from bradzacher April 8, 2026 11:02
@github-actions github-actions bot removed the awaiting response Issues waiting for a reply from the OP or another party label Apr 8, 2026
@zaewc zaewc force-pushed the fix/no-unnecessary-condition-subtype-check branch from 2a1a1f9 to 9f79dd4 Compare April 8, 2026 11:49
Use mutual assignability and union subtype checks instead of reference
equality to detect unnecessary type guards. This catches cases like
interfaces with optional properties (e.g. Narrower assignable to Wider)
and subtypes of union predicate types (e.g. string subtype of
string | number).

Add eslint-disable for false positives caused by custom TypeScript
typings that make structurally different runtime types appear equivalent.
@zaewc zaewc force-pushed the fix/no-unnecessary-condition-subtype-check branch from 9f79dd4 to a07a056 Compare April 8, 2026 12:03
@bradzacher bradzacher added the 1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge label Apr 9, 2026
Copy link
Copy Markdown
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

Copy link
Copy Markdown
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🆒 !

@bradzacher bradzacher merged commit 7c9e06f into typescript-eslint:main Apr 12, 2026
61 of 62 checks passed
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Apr 15, 2026
| datasource | package                          | from   | to     |
| ---------- | -------------------------------- | ------ | ------ |
| npm        | @typescript-eslint/eslint-plugin | 8.56.0 | 8.58.2 |
| npm        | @typescript-eslint/parser        | 8.56.0 | 8.58.2 |


## [v8.58.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8582-2026-04-13)

##### 🩹 Fixes

- **eslint-plugin:** \[no-unnecessary-condition] use assignability checks in checkTypePredicates ([#12147](typescript-eslint/typescript-eslint#12147))
- remove tsbuildinfo cache file from published packages ([#12187](typescript-eslint/typescript-eslint#12187))

##### ❤️ Thank You

- Abhijeet Singh [@cseas](https://github.com/cseas)
- 송재욱

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2) 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.58.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8581-2026-04-08)

##### 🩹 Fixes

- **eslint-plugin:** \[no-unused-vars] fix false negative for type predicate parameter ([#12004](typescript-eslint/typescript-eslint#12004))

##### ❤️ Thank You

- MinJae [@Ju-MINJAE](https://github.com/Ju-MINJAE)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.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.58.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8580-2026-03-30)

##### 🚀 Features

- support TypeScript 6 ([#12124](typescript-eslint/typescript-eslint#12124))

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-regexp-exec] avoid fixing unknown RegExp flags ([#12161](typescript-eslint/typescript-eslint#12161))
- **eslint-plugin:** \[no-extraneous-class] handle index signatures ([#12142](typescript-eslint/typescript-eslint#12142))
- **eslint-plugin:** crash in `no-unnecessary-type-arguments` ([#12163](typescript-eslint/typescript-eslint#12163))

##### ❤️ Thank You

- ej shafran [@ej-shafran](https://github.com/ej-shafran)
- Evyatar Daud [@StyleShit](https://github.com/StyleShit)
- GG ZIBLAKING
- milkboy2564 [@SeolJaeHyeok](https://github.com/SeolJaeHyeok)
- teee32 [@teee32](https://github.com/teee32)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.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.57.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8572-2026-03-23)

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-readonly-parameter-types] preserve type alias infomation ([#11954](typescript-eslint/typescript-eslint#11954))
- **eslint-plugin:** \[no-useless-default-assignment] skip reporting false positives for unresolved type parameters ([#12127](typescript-eslint/typescript-eslint#12127))
- **eslint-plugin:** \[no-unsafe-return] false positive on unwrapping generic ([#12125](typescript-eslint/typescript-eslint#12125))
- **eslint-plugin:** \[no-restricted-types] flag banned generics in extends or implements ([#12120](typescript-eslint/typescript-eslint#12120))
- **eslint-plugin:** \[array-type] ignore Array and ReadonlyArray without type arguments ([#11971](typescript-eslint/typescript-eslint#11971))
- **eslint-plugin:** \[prefer-optional-chain] remove dangling closing parenthesis ([#11865](typescript-eslint/typescript-eslint#11865))

##### ❤️ Thank You

- Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
- Konv Suu
- mdm317
- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)
- SungHyun627 [@SungHyun627](https://github.com/SungHyun627)
- Tamashoo [@Tamashoo](https://github.com/Tamashoo)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.2) 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.57.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8571-2026-03-16)

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-optional-chain] no report for property on intersection type ([#12126](typescript-eslint/typescript-eslint#12126))

##### ❤️ Thank You

- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.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.57.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8570-2026-03-09)

##### 🚀 Features

- **eslint-plugin:** \[no-unnecessary-condition] allow literal loop conditions in for/do loops ([#12080](typescript-eslint/typescript-eslint#12080))

##### 🩹 Fixes

- **eslint-plugin:** \[no-base-to-string] fix false positive for toString with overloads ([#12089](typescript-eslint/typescript-eslint#12089))
- **eslint-plugin:** \[prefer-promise-reject-errors] add allow `TypeOrValueSpecifier` to prefer-promise-reject-errors ([#12094](typescript-eslint/typescript-eslint#12094))
- **typescript-estree:** if the template literal is tagged and the text has an invalid escape, `cooked` will be `null` ([#11355](typescript-eslint/typescript-eslint#11355))
- **eslint-plugin:** guard against negative paramIndex in no-useless-default-assignment ([#12077](typescript-eslint/typescript-eslint#12077))
- **eslint-plugin:** handle statically analyzable computed keys in prefer-readonly ([#12079](typescript-eslint/typescript-eslint#12079))
- **eslint-plugin:** \[strict-void-return] false positives with overloads ([#12055](typescript-eslint/typescript-eslint#12055))

##### ❤️ Thank You

- Brad Zacher [@bradzacher](https://github.com/bradzacher)
- Brian Schlenker [@bschlenk](https://github.com/bschlenk)
- Evyatar Daud [@StyleShit](https://github.com/StyleShit)
- James Henry [@JamesHenry](https://github.com/JamesHenry)
- Josh Goldberg
- Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
- Moses Odutusin [@thebolarin](https://github.com/thebolarin)
- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)
- SungHyun627 [@SungHyun627](https://github.com/SungHyun627)
- Younsang Na [@nayounsang](https://github.com/nayounsang)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.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.56.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8561-2026-02-23)

This was a version bump only for eslint-plugin to align it with other projects, there were no code changes.

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.56.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.
renovate bot added a commit to andrei-picus-tink/auto-renovate that referenced this pull request Apr 19, 2026
| datasource | package                          | from   | to     |
| ---------- | -------------------------------- | ------ | ------ |
| npm        | @typescript-eslint/eslint-plugin | 8.56.0 | 8.58.2 |
| npm        | @typescript-eslint/parser        | 8.56.0 | 8.58.2 |


## [v8.58.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8582-2026-04-13)

##### 🩹 Fixes

- **eslint-plugin:** \[no-unnecessary-condition] use assignability checks in checkTypePredicates ([#12147](typescript-eslint/typescript-eslint#12147))
- remove tsbuildinfo cache file from published packages ([#12187](typescript-eslint/typescript-eslint#12187))

##### ❤️ Thank You

- Abhijeet Singh [@cseas](https://github.com/cseas)
- 송재욱

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.2) 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.58.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8581-2026-04-08)

##### 🩹 Fixes

- **eslint-plugin:** \[no-unused-vars] fix false negative for type predicate parameter ([#12004](typescript-eslint/typescript-eslint#12004))

##### ❤️ Thank You

- MinJae [@Ju-MINJAE](https://github.com/Ju-MINJAE)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.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.58.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8580-2026-03-30)

##### 🚀 Features

- support TypeScript 6 ([#12124](typescript-eslint/typescript-eslint#12124))

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-regexp-exec] avoid fixing unknown RegExp flags ([#12161](typescript-eslint/typescript-eslint#12161))
- **eslint-plugin:** \[no-extraneous-class] handle index signatures ([#12142](typescript-eslint/typescript-eslint#12142))
- **eslint-plugin:** crash in `no-unnecessary-type-arguments` ([#12163](typescript-eslint/typescript-eslint#12163))

##### ❤️ Thank You

- ej shafran [@ej-shafran](https://github.com/ej-shafran)
- Evyatar Daud [@StyleShit](https://github.com/StyleShit)
- GG ZIBLAKING
- milkboy2564 [@SeolJaeHyeok](https://github.com/SeolJaeHyeok)
- teee32 [@teee32](https://github.com/teee32)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.58.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.57.2](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8572-2026-03-23)

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-readonly-parameter-types] preserve type alias infomation ([#11954](typescript-eslint/typescript-eslint#11954))
- **eslint-plugin:** \[no-useless-default-assignment] skip reporting false positives for unresolved type parameters ([#12127](typescript-eslint/typescript-eslint#12127))
- **eslint-plugin:** \[no-unsafe-return] false positive on unwrapping generic ([#12125](typescript-eslint/typescript-eslint#12125))
- **eslint-plugin:** \[no-restricted-types] flag banned generics in extends or implements ([#12120](typescript-eslint/typescript-eslint#12120))
- **eslint-plugin:** \[array-type] ignore Array and ReadonlyArray without type arguments ([#11971](typescript-eslint/typescript-eslint#11971))
- **eslint-plugin:** \[prefer-optional-chain] remove dangling closing parenthesis ([#11865](typescript-eslint/typescript-eslint#11865))

##### ❤️ Thank You

- Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
- Konv Suu
- mdm317
- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)
- SungHyun627 [@SungHyun627](https://github.com/SungHyun627)
- Tamashoo [@Tamashoo](https://github.com/Tamashoo)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.2) 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.57.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8571-2026-03-16)

##### 🩹 Fixes

- **eslint-plugin:** \[prefer-optional-chain] no report for property on intersection type ([#12126](typescript-eslint/typescript-eslint#12126))

##### ❤️ Thank You

- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.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.57.0](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8570-2026-03-09)

##### 🚀 Features

- **eslint-plugin:** \[no-unnecessary-condition] allow literal loop conditions in for/do loops ([#12080](typescript-eslint/typescript-eslint#12080))

##### 🩹 Fixes

- **eslint-plugin:** \[no-base-to-string] fix false positive for toString with overloads ([#12089](typescript-eslint/typescript-eslint#12089))
- **eslint-plugin:** \[prefer-promise-reject-errors] add allow `TypeOrValueSpecifier` to prefer-promise-reject-errors ([#12094](typescript-eslint/typescript-eslint#12094))
- **typescript-estree:** if the template literal is tagged and the text has an invalid escape, `cooked` will be `null` ([#11355](typescript-eslint/typescript-eslint#11355))
- **eslint-plugin:** guard against negative paramIndex in no-useless-default-assignment ([#12077](typescript-eslint/typescript-eslint#12077))
- **eslint-plugin:** handle statically analyzable computed keys in prefer-readonly ([#12079](typescript-eslint/typescript-eslint#12079))
- **eslint-plugin:** \[strict-void-return] false positives with overloads ([#12055](typescript-eslint/typescript-eslint#12055))

##### ❤️ Thank You

- Brad Zacher [@bradzacher](https://github.com/bradzacher)
- Brian Schlenker [@bschlenk](https://github.com/bschlenk)
- Evyatar Daud [@StyleShit](https://github.com/StyleShit)
- James Henry [@JamesHenry](https://github.com/JamesHenry)
- Josh Goldberg
- Kirk Waiblinger [@kirkwaiblinger](https://github.com/kirkwaiblinger)
- Moses Odutusin [@thebolarin](https://github.com/thebolarin)
- Newton Yuan [@NewtonYuan](https://github.com/NewtonYuan)
- SungHyun627 [@SungHyun627](https://github.com/SungHyun627)
- Younsang Na [@nayounsang](https://github.com/nayounsang)

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.57.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.56.1](https://github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/eslint-plugin/CHANGELOG.md#8561-2026-02-23)

This was a version bump only for eslint-plugin to align it with other projects, there were no code changes.

See [GitHub Releases](https://github.com/typescript-eslint/typescript-eslint/releases/tag/v8.56.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.
@ArnaudBarre
Copy link
Copy Markdown
Contributor

@bradzacher
Copy link
Copy Markdown
Member

bradzacher commented Apr 19, 2026

@ArnaudBarre you don't need a predicate at all in the last test case because b is an optional property. So you don't need a runtime assertion at all.

Eg const n: Narrower = w is valid TS code when the "new" property is marked as optional.

If the property is non-optional then the check is required and the rule does not report.

@ArnaudBarre
Copy link
Copy Markdown
Contributor

Oh yeah true, that also explain the edge case (even if I think it would be better implemented by actually checking the the narrower type has at least one required property)

The fact that you need to create a new variable in was not be obvious to me and could be added in the message

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1 approval >=1 team member has approved this PR; we're now leaving it open for more reviews before we merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancement: [no-unnecessary-condition] use assignability checks in checkTypePredicates

4 participants