Skip to content

ci(qodo): add optional chaining compliance rule - #12255

Closed
pkuczynski wants to merge 3 commits into
masterfrom
chore/optional-chaining-compliance
Closed

pkuczynski wants to merge 3 commits into
masterfrom
chore/optional-chaining-compliance

Conversation

@pkuczynski

Copy link
Copy Markdown
Member

Add a Qodo compliance check for preferring optional chaining (?.) over non-null assertions (!) in tests after null checks.

SonarQube frequently flags non-null assertions as unnecessary type assertions (typescript:S4325). Adding this to the Qodo checklist catches it earlier during PR review, before it reaches the SonarQube quality gate.

const result = await repository.findOne({ where: { id: 1 } })

// Before (non-null assertion)
expect(result!.name).to.equal("foo")

// After (optional chaining)
expect(result).to.not.be.null
expect(result?.name).to.equal("foo")

@pkuczynski pkuczynski self-assigned this Mar 22, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Add optional chaining compliance rule to Qodo checklist

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add optional chaining compliance rule to Qodo checklist
• Catches non-null assertion issues before SonarQube quality gate
• Aligns with TypeScript best practices for null safety
Diagram
flowchart LR
  A["PR Compliance Checklist"] -->|adds new rule| B["Optional Chaining Rule"]
  B -->|objective| C["Use ?. instead of !"]
  B -->|success| D["expect().to.not.be.null + ?.property"]
  B -->|failure| E["result!.property usage"]
Loading

Grey Divider

File Changes

1. pr_compliance_checklist.yaml ✨ Enhancement +6/-0

Add optional chaining compliance rule

• Added new compliance rule for optional chaining preference in tests
• Rule specifies using optional chaining (?.) instead of non-null assertions (!)
• Includes objective, success criteria, and failure criteria for the rule
• Positioned between "Consistent Naming Conventions" and "Remove AI-generated noise" rules

pr_compliance_checklist.yaml


Grey Divider

Qodo Logo

@pkuczynski pkuczynski added this to the 1.0 milestone Mar 22, 2026
@pkuczynski
pkuczynski enabled auto-merge (squash) March 22, 2026 22:28
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Mar 22, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Skipped loop assertions ☑ 🐞 Bug ✓ Correctness
Description
The success criteria recommends result?.items ?? [] for iteration, which can cause loop bodies
(and any assertions inside) to be skipped when result is null, allowing tests to pass without
exercising checks.
Code

pr_compliance_checklist.yaml[R23-24]

+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
+      failure_criteria: "Tests use result!.property or result!.items non-null assertions after null checks."
Evidence
The checklist text explicitly endorses result?.items ?? [] as a preferred iteration pattern. In
JS/TS, (null as any)?.items evaluates to undefined, and undefined ?? [] becomes [], so `for
(... of [])` performs zero iterations and silently skips any assertions that were supposed to run.

pr_compliance_checklist.yaml[20-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The compliance rule’s success criteria recommends iterating with `result?.items ?? []`, which can silently skip the loop body when `result` is `null`, allowing tests to pass without executing assertions.
### Issue Context
This is a checklist rule, so its wording will influence how reviewers/automation judge PRs. The recommended iteration pattern weakens tests by turning missing data into an empty collection.
### Fix Focus Areas
- pr_compliance_checklist.yaml[20-24]
### Suggested change
Update the success/failure criteria to avoid recommending `?? []` for iteration after a null-check expectation. Prefer either:
- using a real guard (e.g., assert non-null then use `result.items`), or
- explicitly failing when `items` is missing, rather than defaulting to `[]`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Non-null assertion overreach ☑ 🐞 Bug ⚙ Maintainability
Description
The objective/failure criteria reads like a blanket replacement of ! with ?. after null checks,
but optional chaining is not a semantic replacement for non-null assertions used for mutation or for
passing values to APIs, making the rule impractical and likely to cause noisy enforcement.
Code

pr_compliance_checklist.yaml[R22-24]

+      objective: "Tests should use optional chaining (?.) instead of non-null assertions (!) after null checks."
+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
+      failure_criteria: "Tests use result!.property or result!.items non-null assertions after null checks."
Evidence
Existing tests commonly do a runtime null check and then legitimately use ! for non-assertion code
paths (mutation / passing an entity), where ?. cannot be used. Example: after
expect(loadedPost).to.not.be.null, the test mutates and saves via loadedPost! and
save(loadedPost!), which optional chaining cannot replace without changing behavior.

pr_compliance_checklist.yaml[20-24]
test/functional/embedded/outer-primary-column/outer-primary-column.test.ts[67-81]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new compliance rule suggests replacing non-null assertions (`!`) with optional chaining (`?.`) after null checks, but optional chaining only applies to safe access. Many tests legitimately need `!` after a null check for mutation or to pass values to functions.
### Issue Context
Repo tests already contain patterns like:
- `expect(x).to.not.be.null`
- `x!.prop = ...`
- `repo.save(x!)`
These uses of `!` are not replaceable by `?.`.
### Fix Focus Areas
- pr_compliance_checklist.yaml[20-24]
### Suggested change
Update wording so the rule only targets **property access in assertions** (the Sonar S4325 scenario) and explicitly exempts mutation/function-argument uses of `!` after a runtime null check.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

3. Null-check style too specific ☑ 🐞 Bug ⚙ Maintainability
Description
The success criteria hardcodes expect(result).to.not.be.null, but the repo uses both
expect(x).to.not.be.null and expect(x).not.to.be.null; if the compliance checker matches
strictly, it will produce false positives for existing valid style.
Code

pr_compliance_checklist.yaml[23]

+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
Evidence
The test suite contains both Chai spellings: e.g., expect(post).not.to.be.null in the transaction
isolation tests, and expect(loadedPost).to.not.be.null in embedded tests. A strict
string/pattern-based checker keyed to only one spelling will misclassify compliant code.

pr_compliance_checklist.yaml[23-23]
test/functional/transaction/database-specific-isolation/oracle-isolation.test.ts[45-52]
test/functional/embedded/outer-primary-column/outer-primary-column.test.ts[67-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The success criteria example uses only `expect(result).to.not.be.null`, but the repo also uses `expect(x).not.to.be.null`. If compliance enforcement is pattern-based, this will create false positives.
### Issue Context
Both spellings are valid and already used throughout the test suite.
### Fix Focus Areas
- pr_compliance_checklist.yaml[23-23]
### Suggested change
Reword success criteria to accept both forms (e.g., “`expect(x).to.not.be.null` or `expect(x).not.to.be.null`”), or make it generic (“assert `x` is not null”) rather than prescribing an exact token sequence.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Grey Divider

Previous review results

Review updated until commit b0a81dc

Results up to commit N/A


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider
Action required
1. Skipped loop assertions ☑ 🐞 Bug ✓ Correctness
Description
The success criteria recommends result?.items ?? [] for iteration, which can cause loop bodies
(and any assertions inside) to be skipped when result is null, allowing tests to pass without
exercising checks.
Code

pr_compliance_checklist.yaml[R23-24]

+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
+      failure_criteria: "Tests use result!.property or result!.items non-null assertions after null checks."
Evidence
The checklist text explicitly endorses result?.items ?? [] as a preferred iteration pattern. In
JS/TS, (null as any)?.items evaluates to undefined, and undefined ?? [] becomes [], so `for
(... of [])` performs zero iterations and silently skips any assertions that were supposed to run.

pr_compliance_checklist.yaml[20-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The compliance rule’s success criteria recommends iterating with `result?.items ?? []`, which can silently skip the loop body when `result` is `null`, allowing tests to pass without executing assertions.
### Issue Context
This is a checklist rule, so its wording will influence how reviewers/automation judge PRs. The recommended iteration pattern weakens tests by turning missing data into an empty collection.
### Fix Focus Areas
- pr_compliance_checklist.yaml[20-24]
### Suggested change
Update the success/failure criteria to avoid recommending `?? []` for iteration after a null-check expectation. Prefer either:
- using a real guard (e.g., assert non-null then use `result.items`), or
- explicitly failing when `items` is missing, rather than defaulting to `[]`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Non-null assertion overreach ☑ 🐞 Bug ⚙ Maintainability
Description
The objective/failure criteria reads like a blanket replacement of ! with ?. after null checks,
but optional chaining is not a semantic replacement for non-null assertions used for mutation or for
passing values to APIs, making the rule impractical and likely to cause noisy enforcement.
Code

pr_compliance_checklist.yaml[R22-24]

+      objective: "Tests should use optional chaining (?.) instead of non-null assertions (!) after null checks."
+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
+      failure_criteria: "Tests use result!.property or result!.items non-null assertions after null checks."
Evidence
Existing tests commonly do a runtime null check and then legitimately use ! for non-assertion code
paths (mutation / passing an entity), where ?. cannot be used. Example: after
expect(loadedPost).to.not.be.null, the test mutates and saves via loadedPost! and
save(loadedPost!), which optional chaining cannot replace without changing behavior.

pr_compliance_checklist.yaml[20-24]
test/functional/embedded/outer-primary-column/outer-primary-column.test.ts[67-81]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new compliance rule suggests replacing non-null assertions (`!`) with optional chaining (`?.`) after null checks, but optional chaining only applies to safe access. Many tests legitimately need `!` after a null check for mutation or to pass values to functions.
### Issue Context
Repo tests already contain patterns like:
- `expect(x).to.not.be.null`
- `x!.prop = ...`
- `repo.save(x!)`
These uses of `!` are not replaceable by `?.`.
### Fix Focus Areas
- pr_compliance_checklist.yaml[20-24]
### Suggested change
Update wording so the rule only targets **property access in assertions** (the Sonar S4325 scenario) and explicitly exempts mutation/function-argument uses of `!` after a runtime null check.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments
3. Null-check style too specific ☑ 🐞 Bug ⚙ Maintainability
Description
The success criteria hardcodes expect(result).to.not.be.null, but the repo uses both
expect(x).to.not.be.null and expect(x).not.to.be.null; if the compliance checker matches
strictly, it will produce false positives for existing valid style.
Code

pr_compliance_checklist.yaml[23]

+      success_criteria: "Test assertions use expect(result).to.not.be.null followed by result?.property and result?.items ?? [] for iteration."
Evidence
The test suite contains both Chai spellings: e.g., expect(post).not.to.be.null in the transaction
isolation tests, and expect(loadedPost).to.not.be.null in embedded tests. A strict
string/pattern-based checker keyed to only one spelling will misclassify compliant code.

pr_compliance_checklist.yaml[23-23]
test/functional/transaction/database-specific-isolation/oracle-isolation.test.ts[45-52]
test/functional/embedded/outer-primary-column/outer-primary-column.test.ts[67-72]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The success criteria example uses only `expect(result).to.not.be.null`, but the repo also uses `expect(x).not.to.be.null`. If compliance enforcement is pattern-based, this will create false positives.
### Issue Context
Both spellings are valid and already used throughout the test suite.
### Fix Focus Areas
- pr_compliance_checklist.yaml[23-23]
### Suggested change
Reword success criteria to accept both forms (e.g., “`expect(x).to.not.be.null` or `expect(x).not.to.be.null`”), or make it generic (“assert `x` is not null”) rather than prescribing an exact token sequence.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider Grey Divider

Qodo Logo

@alumni alumni left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Qodo is not for static checks.

There are built-in eslint rules for that, which we might want to enable sometime in the future.

I would not merge this.

@alumni
alumni disabled auto-merge March 22, 2026 23:03
@alumni alumni closed this Mar 22, 2026
@pkuczynski

Copy link
Copy Markdown
Member Author

Qodo is not for static checks.

There are built-in eslint rules for that, which we might want to enable sometime in the future.

Should we also then remove https://github.com/typeorm/typeorm/blob/master/pr_compliance_checklist.yaml#L14-L18?

And how would you like to implement in eslint a check where in the test (only!) we should first assert for not null, and then use optional chaining? I really want to see an example of such config...

@pkuczynski pkuczynski reopened this Mar 23, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Add optional chaining compliance rule to Qodo checklist

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add optional chaining compliance rule to Qodo checklist
• Catches non-null assertion issues before SonarQube quality gate
• Promotes safer TypeScript patterns in test code
Diagram
flowchart LR
  A["PR Compliance Checklist"] -->|adds new rule| B["Optional Chaining Rule"]
  B -->|objective| C["Use ?. instead of !"]
  B -->|success criteria| D["expect().to.not.be.null + optional chaining"]
  B -->|failure criteria| E["Non-null assertions detected"]
Loading

Grey Divider

File Changes

1. pr_compliance_checklist.yaml ✨ Enhancement +6/-0

Add optional chaining compliance rule

• Added new compliance rule for optional chaining in tests
• Rule enforces using optional chaining (?.) instead of non-null assertions (!)
• Includes objective, success criteria, and failure criteria for the rule
• Positioned between "Consistent Naming Conventions" and "Remove AI-generated noise" rules

pr_compliance_checklist.yaml


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Persistent review updated to latest commit d731fae

@alumni

alumni commented Mar 24, 2026 •

Copy link
Copy Markdown
Collaborator

How about eslint: @typescript-eslint/prefer-optional-chain. Might as well add @typescript-eslint/prefer-nullish-coalescing since it's similar.

Normally we would add the entire config at once (@typescript-eslint/stylistic-type-checked), but our code base might not be there yet (the internal types are not always correct, so some rule that might be stylistic based on the type information, could end up breaking functionality)

I also noticed there's a lot of .substr(0, 1) which could be fixed with @typescript-eslint/prefer-string-starts-ends-with, in the same config.

Sonar also complains quite a lot about unnecessary type assertions, e.g. @typescript-eslint/no-unnecessary-type-assertion - this is in the recommended config, but was disabled on purpose since removing the assertions would cause more trouble than keeping them. This one in particular we should delay enabling for a while - especially with the new TS version (v6 was just released and we can't upgrade yet).

Should we also then remove https://github.com/typeorm/typeorm/blob/master/pr_compliance_checklist.yaml#L14-L18?

That would great, that Qodo rule is so generic that I doubt is of any use. "project's established naming standards" - what's that mean? They are not described anywhere. @typescript-eslint/naming-convention is the way.

And how would you like to implement in eslint a check where in the test (only!) we should first assert for not null, and then use optional chaining?

typescript-eslint uses the inferred types in each line. So if the assertion is properly typed, you'd get the right inference. Unfortunately, chai isn't that great. Inference is good with assert assertions, but we tend to use expect and should assertions which are closer to natural language.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Mar 29, 2026 •

Copy link
Copy Markdown

Deploying typeorm with  Cloudflare Pages  Cloudflare Pages

Latest commit: e8e4aa9
Status: ✅  Deploy successful!
Preview URL: https://73271202.typeorm.pages.dev
Branch Preview URL: https://chore-optional-chaining-comp.typeorm.pages.dev

View logs

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Persistent review updated to latest commit e8e4aa9

@sonarqubecloud

Copy link
Copy Markdown

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Persistent review updated to latest commit b0a81dc

@pkuczynski

pkuczynski commented Apr 9, 2026 •

Copy link
Copy Markdown
Member Author

I've been enabling these rules incrementally in separate PRs:

@typescript-eslint/prefer-nullish-coalescing was attempted but dropped — 642 violations that can't be auto-fixed due to intentional || usage with falsy primitives.

I also tackled the non-null assertion issue from SonarQube by replacing findOne/getOne with OrFail variants across the test suite in #12325.

Closing in favor of the PRs above.

@pkuczynski pkuczynski closed this Apr 9, 2026
@pkuczynski
pkuczynski deleted the chore/optional-chaining-compliance branch April 9, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants