Skip to content

fix: resolve all strict TypeScript errors#69

Merged
jan-kubica merged 4 commits intomainfrom
fix/strict-mode-indexed-access
Mar 23, 2026
Merged

fix: resolve all strict TypeScript errors#69
jan-kubica merged 4 commits intomainfrom
fix/strict-mode-indexed-access

Conversation

@jan-kubica
Copy link
Copy Markdown
Contributor

@jan-kubica jan-kubica commented Mar 23, 2026

Summary

  • Use .charAt() instead of bracket indexing on strings to avoid string | undefined under noUncheckedIndexedAccess
  • Add ?? 0 fallback for readonly tuple/array weight lookups in bounded loops
  • Add explicit undefined guard for cy/vat checkLetter
  • Fix de/idnr digit distribution counter to avoid unchecked indexed increment
  • Remove unused imports (randomDigits in ba/jmbg and ro/cnp, isdigits in mu/brn)

The tsconfig already had strict: true and noUncheckedIndexedAccess: true enabled; these 25 files had latent type errors. Now tsc --noEmit passes cleanly.

Test plan

  • tsc --noEmit reports zero errors
  • bun test passes all 4184 tests (1 pre-existing failure in de.handelsreg unrelated to this change)

Open with Devin

- Use `.charAt()` instead of bracket indexing on strings to avoid
  `string | undefined` return type
- Add `?? 0` fallback for readonly tuple/array weight lookups in
  bounded loops
- Add explicit undefined guard for `cy/vat` checkLetter
- Fix `de/idnr` digit distribution counter to avoid unchecked
  indexed increment
- Remove unused imports: `randomDigits` (ba/jmbg, ro/cnp),
  `isdigits` (mu/brn)

All 4184 tests pass. The tsconfig already had `strict: true` and
`noUncheckedIndexedAccess: true` enabled; these fixes make the
codebase actually clean under those settings.
devin-ai-integration[bot]

This comment was marked as resolved.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 23, 2026

Greptile Summary

This PR systematically resolves 25 files' worth of latent TypeScript strict-mode errors introduced by noUncheckedIndexedAccess. The three fix strategies used are: replacing str[i] with str.charAt(i) for string character reads, adding ?? 0 fallbacks for const-tuple weight lookups in bounded loops, and removing genuinely unused imports (randomDigits in ba/jmbg and ro/cnp, isdigits in mu/brn). Two new tsconfig flags — noImplicitOverride and noImplicitReturns — are also introduced, though not mentioned in the PR description.

Key observations:

  • The fix is mechanically correct and all 4184 tests continue to pass.
  • Two accesses in src/is/kennitala.ts (v[8] on line 48 and v[9] on line 59) were not converted to .charAt(). They compile without errors only because Number() accepts any, but they are inconsistent with every other change in the PR.
  • src/cy/vat.ts uses a different strategy for its checkLetter = v[8] — an explicit === undefined guard — rather than converting to .charAt(8), creating a mixed pattern.
  • The tsconfig.json additions of noImplicitOverride and noImplicitReturns are positive improvements but were not documented in the PR description.

Confidence Score: 5/5

  • Safe to merge — all changes are type-safety fixes with no logic changes; 4184 tests pass.
  • All changes are mechanical substitutions (bracket → charAt, array access → ?? 0 fallback) within loops already bounded to the array's length. No algorithm logic is altered. The two remaining bracket accesses in kennitala.ts and the mixed strategy in cy/vat.ts are style inconsistencies, not bugs. The unused-import removals are verified correct. The tsc and test suite both confirm cleanliness.
  • src/is/kennitala.ts (lines 48, 59) and src/cy/vat.ts (line 49) have minor style inconsistencies worth tidying up.

Important Files Changed

Filename Overview
src/is/kennitala.ts Loop converted to v.charAt(i) + ?? 0, but lines 48 and 59 still use v[8]/v[9] bracket notation — passes tsc only because Number() accepts any.
src/cy/vat.ts Mixed fix strategy: checkLetter = v[8] kept as bracket notation with an added === undefined guard, while every other access in the file and PR uses .charAt().
src/de/idnr.ts Digit distribution counter correctly refactored to avoid unchecked increment — reads prev, guards on undefined, then assigns; logically equivalent and TypeScript-clean.
src/ba/jmbg.ts Removed unused randomDigits import; generate now builds components via randomInt, so the removal is correct.
src/ro/cnp.ts Removed unused randomDigits import; generate already builds each component via randomInt, so removal is correct.
src/mu/brn.ts Removed unused isdigits import; validation uses regex patterns exclusively, so removal is correct.
tsconfig.json Added noImplicitOverride and noImplicitReturns (two undocumented additions beyond the stated scope of the PR); both are sensible strictness flags and the test suite confirms no breakage.
src/au/abn.ts Standard .charAt(i) / ?? 0 fix applied consistently to all weight and digit accesses.
src/au/acn.ts .charAt(i) and ?? 0 applied to both validate and generate loops; check digit comparison also updated.
src/es/cif.ts String bracket accesses (first, last, CIF_LETTERS[check]) replaced with v.charAt() and CIF_LETTERS.charAt(check) consistently.
src/es/vat.ts All five DNI_LETTERS[…] and CIF_LETTERS[…] lookup sites replaced with .charAt(); first and last character reads also updated.
src/br/cnpj.ts Both weight-sum loops and the out-of-loop w2[12] access guarded with ?? 0; .charAt() applied to all digit reads.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["String character access\n(was: v[i] → string | undefined)"] --> B{Access Pattern}
    B -->|"Character from string"| C["v.charAt(i)\n→ always string"]
    B -->|"Weight from const tuple"| D["WEIGHTS[i] ?? 0\n→ always number"]
    B -->|"Letter from string constant"| E["LETTERS.charAt(idx)\n→ always string"]

    C --> F["No undefined risk\nPassed to Number()"]
    D --> G["No undefined risk\nUsed in arithmetic"]
    E --> H["No undefined risk\nUsed in comparison"]

    F & G & H --> I["tsc --noEmit passes\nnoUncheckedIndexedAccess satisfied"]

    style A fill:#f88,color:#000
    style I fill:#8f8,color:#000
Loading

Comments Outside Diff (1)

  1. src/cy/vat.ts, line 49 (link)

    P2 Mixed fix strategy for v[8]

    The line const checkLetter = v[8] still uses bracket notation, giving checkLetter the type string | undefined. The PR addressed this by adding an explicit === undefined guard in the if condition below (line 56), which is correct and type-safe. However, this is the only place in the entire PR that uses the "add a guard" strategy — all 20+ other sites converted to .charAt() instead.

    If you'd prefer full consistency, the straightforward change is:

    (and then the checkLetter === undefined guard on line 56 could be dropped, since charAt always returns a string). Either approach is fine, but mixing both patterns across the codebase may confuse future readers.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (2): Last reviewed commit: "fix: remove redundant strictNullChecks a..." | Re-trigger Greptile

greptile-apps[bot]

This comment was marked as resolved.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

Both are already implied by strict: true. Keeping them explicitly
was misleading, as Greptile flagged in review.
@jan-kubica jan-kubica merged commit 971cd34 into main Mar 23, 2026
4 of 6 checks passed
Comment thread src/is/kennitala.ts
sum += Number(v.charAt(i)) * (WEIGHTS[i] ?? 0);
}
const remainder = (11 - (sum % 11)) % 11;
if (remainder === 10 || remainder !== Number(v[8])) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Inconsistent bracket-index conversion

The loop immediately above (line 45) was converted to use v.charAt(i), but the two remaining character accesses on lines 48 and 59 still use bracket notation (v[8] / v[9]). Under noUncheckedIndexedAccess these return string | undefined; the only reason they don't produce TypeScript errors here is that they are immediately wrapped in Number(), which is typed as accepting any.

For consistency with every other changed file in this PR—and to avoid silent NaN if the guard is ever removed—they should also be converted:

Suggested change
if (remainder === 10 || remainder !== Number(v[8])) {
if (remainder === 10 || remainder !== Number(v.charAt(8))) {

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 0 new potential issues.

View 3 additional findings in Devin Review.

Open in Devin Review

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant