guards: accept readonly arrays (and other array-likes) in isEmpty / hasElements / safeLength

Open Beginner friendly
#943 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript
Domain
frontend

Research direction

The guards live in src/utils/guards.ts; widen the isEmpty, safeLength and hasElements parameter types there and leave runtime behaviour alone. The two // guards-ok call sites to convert are src/bus/deriveEvents.ts line 82 and src/utils/abciQuery.ts line 70. Add readonly-array, Uint8Array, null and undefined cases to guards.test.ts, then confirm tsc -b passes.

Written by the indexing model from the issue text.

Description

enhancement tech debt

Problem

isEmpty, hasElements and safeLength in src/utils/guards.ts only accept a mutable T[]:

export function isEmpty<T>(arr: T[] | null | undefined): boolean
export function hasElements<T>(arr: T[] | null | undefined): arr is T[]
export function safeLength<T>(arr: T[] | null | undefined): number

A readonly T[], which is what SDK DTO arrays and as const data usually are, isn't assignable to T[]. Typed arrays (Uint8Array) aren't either. Code holding one of those can't use the guard, so it keeps the raw check marked // guards-ok.

Two of the four // guards-ok lines left by the #937 migration are there only because of this:

  • src/bus/deriveEvents.ts:82: if (!actions || actions.length === 0) { // guards-ok. actions is a readonly ActionDTO[] (#940).
  • src/utils/abciQuery.ts:70: if (bytes.length === 0) return ""; // guards-ok. bytes is a Uint8Array (#941).

The other two (audio/actionSoundPlayer.ts, components/playPage/TableErrorPage.tsx) are legitimate: there, null and undefined mean different things.

Proposal

Widen the parameter types. Runtime behaviour doesn't change.

export function isEmpty(arr: ArrayLike<unknown> | null | undefined): boolean
export function safeLength(arr: ArrayLike<unknown> | null | undefined): number
export function hasElements<A extends readonly unknown[]>(arr: A | null | undefined): arr is A

hasElements keeps narrowing to the caller's own array type, readonly or not.

Then:

  • Convert the two lines above and drop their // guards-ok.
  • Add cases to guards.test.ts for a readonly array, a Uint8Array, null and undefined.
  • Check that tsc -b still passes at every existing call site. Any caller that relied on hasElements narrowing to a mutable array will show up there.

Related: #937 (guard migration), #940, #941.

🤖 Generated with Claude Code

Dominant language
TypeScript
Stars
2
Forks
5
Avg merge
22h 35m
Merged PRs (30d)
91

Getting set up

We have not checked this project's setup files yet. Start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from block52/ui

All issues in block52/ui

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.