Skip to content

A sixth client-SDK erasure spelling, larger than the other five combined: 43 exported methods return res.json() directly, whose lib.dom type is Promise< any > #12104

Description

@yinlianghui

Found while building the client-side check:exported-any equivalent (#11927). Out of that card's
declared scope and deliberately not fixed there
— that card builds the ratchet and seeds its
ledger; this is the worklist the ledger now names and nothing schedules.

Measured at 0e0bf8049, on the builtpackages/client/dist/index.d.ts.

ⓘ Angle brackets are spaced throughout (Promise< any >) on purpose — GitHub's body sanitizer
strips tag-shaped spans, backticks and fenced code included. The first write of this card lost
every unspaced one; this is the repaired text.

The spelling

#8140's census enumerated four spellings, all putting any inside a Promise< … > return
annotation where a grep can see it. #11925 added a fifth: no annotation, type inferred from
this.unwrapResponse< …any… >(res).

There is a sixth, and it is the largest single class:

setActive: async(organizationId: string)=>{constres=awaitthis.fetch(...);returnres.json();// lib.dom: Response.json(): Promise< any >},

The method carries no return annotation and never names any, Promise, or unwrapResponse, so
it is invisible to every grep #8140's census and #11925 used. Its published type is
Promise< any > all the same, because Response.json() is declared Promise< any > in lib.dom.

Count and how it was taken

Not a grep — the count comes from asking the type checker what each export resolves to on the
built declaration, which is the whole reason #11927 reads a built dist:

$ pnpm --filter @objectstack/client check:exported-any-returns
❌ 65 exported callable(s) of @objectstack/client resolve to `any` …

Classified by mechanism against packages/client/src/index.ts, the 65 split:

mechanismncard
declared Promise< any >, deliberate — no contract exists to bind4#11924
no annotation, return this.unwrapResponse< any >(res)17#11925
no annotation, return res.json()43this card
no annotation, delegates to an already-erased sibling (organizations.invitations.resendorganizations.invite)1closed out by whichever card binds invite

The 43

  • analytics.*query, meta, explain, queryDataset (4)
  • organizations.*create, update, setActive, get, listMembers, invite, leave,
    delete, removeMember, updateMemberRole, getActiveMember (11)
  • organizations.invitations.*cancel, accept, reject (3)
  • organizations.teams.*create, update, delete, addMember, removeMember (5)
  • oauth.*applications.register, applications.get, applications.getPublic,
    applications.delete, consent (5)
  • auth.*updateUser, changePassword, setInitialPassword, changeEmail,
    sendVerificationEmail, verifyEmail, deleteUser (7)
  • auth.sessions.*revoke, revokeOthers, revokeAll (3)
  • auth.twoFactor.*verifyTotp, disable, verifyBackupCode (3)
  • auth.accounts.unlink (1)
  • automation.trigger (1)

Why it is worth its own card rather than a rider on #11925

Note on the overlap with #11925

#11925 counts 38 unannotated unwrapResponse< …any… > methods; #11927's gate flags 17 of
them. The other 21 are typed { package: any } / { packages: any[]; total } / { project: any }
— return types that CONTAIN any rather than BEING any, which is a deliberate and documented
scope line in both check:exported-any gates (a type with any inside it is a far broader
question, and admitting it costs the gate its zero-false-positive property). Those 21 are #11925's
alone and are not ledgered by #11927 — recorded here so the two counts are not read as
contradicting each other.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions