Skip to content

A fifth client-SDK erasure spelling no grep in #8140's census counted: 38 methods with NO return annotation, typed from unwrapResponse< …any… > #11925

Description

@os-zhuang

Found while implementing #8140. Out of its declared scope and deliberately not fixed there
recording it because it is larger than the population that card worked from, and it is invisible to
every grep either the card or its census used.

Measured at 1f6d04703, in packages/client/src/index.ts.

The four spellings #8140 worked from, and the one underneath them

#8140's census enumerated four spellings of return-type erasure, all of which put the any
in a Promise< … > return annotation where a grep can see it:

spelling count at head
): Promise< any > => 32
): Promise< any[] > => 5
): Promise< { …: any[]; … } > => 4
< T = any >(…) on fixed-shape methods 14

A fifth population has no return annotation at all. The method's public return type is
inferred from the type argument handed to the private unwrap helper:

list: async (filters?: { status?: string; type?: string; enabled?: boolean }) => {
    // …
    return this.unwrapResponse< { packages: any[]; total: number } >(res);
},

That method's published type is Promise< { packages: any[]; total: number } >, exactly as erased
as the annotated siblings — but it matches no Promise< … > grep, because the text Promise never
appears in it.

Count: 38 methods. Commands and raw output:

$ grep -cE '(unwrapResponse|_unwrap)<[^>]*\bany\b' packages/client/src/index.ts
68

68 sites total; 30 of them sit inside methods that DO carry an annotation (those are #8140's
population, now bound). The remaining 38 carry none. Reproducer for the split — it finds each
site's enclosing name: async ( declaration and asks whether the signature contains ): Promise<:

$ python3 - <<'PY'
import re
lines = open('packages/client/src/index.ts').read().split('\n')
sites = [i+1 for i,l in enumerate(lines) if re.search(r'(unwrapResponse|_unwrap)<[^>]*\bany\b', l)]
def decl_start(n):
    for j in range(n-1, max(0,n-120), -1):
        if re.match(r'\s*[A-Za-z_][A-Za-z0-9_]*\s*:\s*async\s*(?:<[^>]*>)?\s*\(', lines[j]): return j
    return None
noann = []
for s in sites:
    d = decl_start(s)
    if d is None: continue
    sig = '\n'.join(lines[d:s]); head = sig.split('=> {')[0] if '=> {' in sig else sig
    if not re.search(r'\)\s*:\s*Promise<', head):
        noann.append((s, re.match(r'\s*([A-Za-z_][A-Za-z0-9_]*)\s*:', lines[d]).group(1)))
print('unwrap-any sites total       :', len(sites))
print('…with NO Promise< annotation :', len(noann))
PY
unwrap-any sites total       : 68
…with NO Promise< annotation : 38

Which families, and why they were not swept into #8140

family n notes
meta.* history / diagnostics — getPublished, listDrafts, migrateStored, getDiagnostics, getReferences, getBookTree, getAudit, rollbackItem, diffItem 9 bare unwrapResponse< any >
packages.*list, get, install, enable, disable, update, publish, discardDrafts, listCommits, revertCommit, rollback, export, adoptOrphans, duplicate 14 mostly { package: any } / { packages: any[]; total } / { commits: any[] }
cloud projects.*list, create, update, activate, rotateCredential, updateHostname, updateVisibility, retryProvisioning 8 { project: any }, { project: any; database: any }, { credential: any }
environment-scoped packages — list, install, get, enable, disable, upgrade 6 { package: any } / { packages: any[]; total }
ScopedProjectClient.packages.get 1 { package: any }

#8140's dispatch scoped it to the four annotated spellings, and its clause-② narrowing analysis was
answered against those. Widening mid-card would have shipped a narrowing nobody had reasoned about.
⭐ Note one of these was reachable and is now bound —
ScopedProjectClient.packages.list carried BOTH an annotation and the type argument, so it fell in
scope and became { packages: InstalledPackage[]; total: number }. Its immediate neighbour
packages.get did not, purely because it lacks the annotation. That asymmetry inside one object
literal is the clearest statement of the problem.

Why it is worth its own card


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions