Background
The CLI already uses @sentry/api for all API calls (~60+ SDK functions in production). However, 30 as unknown as casts remain in src/lib/api/ — TypeScript bridges between SDK-generated response types and the CLI's internal SentryXxx wrapper types. These casts don't hide real bugs (the runtime shapes match), but they prevent TypeScript from catching future regressions and make the codebase harder to maintain.
This issue tracks the work to get to zero casts and fully adopt the SDK-generated types.
Three categories of work
Category 1 — Response type casts (~20 casts, CLI-only, ~2 days)
Pattern: return data as unknown as SentryTeam[]
The CLI defines SentryTeam = Partial<SdkTeam> & { id, slug, name }. TypeScript can't automatically narrow from the SDK's strict union type to this intersection. At runtime the shapes are identical.
Fix: Make unwrapResult<T> generic so the single cast lives in infrastructure, and all callers are clean:
// infrastructure.ts
export function unwrapResult<T>(result: SdkResult<unknown>, context: string): T {
if (result.error !== undefined) { throwApiError(...) }
return result.data as T; // one cast, all callers clean
}
// teams.ts — before
return data as unknown as SentryTeam[];
// after
return unwrapResult<SentryTeam[]>(result, "Failed to list teams");
Also simplify wrapper types that are already 100% compatible with SDK types:
SentryTeam → alias SdkTeam directly
SentryRelease → alias Partial<SdkReleaseResponse>
SentryDeploy → alias SDK type directly
Category 2 — Request body casts (~7 casts, needs backend spec fix)
Pattern: body as unknown as Parameters<typeof sdkFn>[0]["body"]
The SDK marks certain request body fields as required (per the spec), but the CLI passes them as optional and the API silently accepts it. Fixing the spec makes the cast unnecessary.
Backend spec changes needed (getsentry/sentry PRs):
| Endpoint |
Field(s) to mark optional |
createANewDashboardForAnOrganization |
widgets |
editAnOrganization_sCustomDashboard |
various fields |
createANewReleaseForAnOrganization |
projects |
updateAnOrganization_sRelease |
various fields |
After each backend fix ships in a new @sentry/api release, the corresponding CLI cast can be removed.
Category 3 — Path parameter casts (3 casts, CLI-only, trivial)
Pattern: dashboard_id: dashboardId as unknown as number
Already blocked on sentry#116836 (dashboard_id type fix). Once that ships:
// Before
dashboard_id: dashboardId as unknown as number,
// After
dashboard_id: dashboardId, // string, as the spec now correctly declares
Wrapper types that need to stay (for now)
Three types keep fields beyond the SDK spec that have never been formally documented:
| Type |
Extra fields |
Fix |
SentryOrganization |
allowMemberProjectCreation, orgRole |
Contribute to Sentry backend spec |
SentryProject |
status (undocumented API field) |
Contribute to Sentry backend spec |
SentryEvent |
dateCreated, fingerprints, sdkUpdates, typed contexts |
Some may be intentionally undocumented; contribute where possible |
Work order
Success metric
grep -c "as unknown as" src/lib/api/*.ts # 0
Background
The CLI already uses
@sentry/apifor all API calls (~60+ SDK functions in production). However, 30as unknown ascasts remain insrc/lib/api/— TypeScript bridges between SDK-generated response types and the CLI's internalSentryXxxwrapper types. These casts don't hide real bugs (the runtime shapes match), but they prevent TypeScript from catching future regressions and make the codebase harder to maintain.This issue tracks the work to get to zero casts and fully adopt the SDK-generated types.
Three categories of work
Category 1 — Response type casts (~20 casts, CLI-only, ~2 days)
Pattern:
return data as unknown as SentryTeam[]The CLI defines
SentryTeam = Partial<SdkTeam> & { id, slug, name }. TypeScript can't automatically narrow from the SDK's strict union type to this intersection. At runtime the shapes are identical.Fix: Make
unwrapResult<T>generic so the single cast lives in infrastructure, and all callers are clean:Also simplify wrapper types that are already 100% compatible with SDK types:
SentryTeam→ aliasSdkTeamdirectlySentryRelease→ aliasPartial<SdkReleaseResponse>SentryDeploy→ alias SDK type directlyCategory 2 — Request body casts (~7 casts, needs backend spec fix)
Pattern:
body as unknown as Parameters<typeof sdkFn>[0]["body"]The SDK marks certain request body fields as required (per the spec), but the CLI passes them as optional and the API silently accepts it. Fixing the spec makes the cast unnecessary.
Backend spec changes needed (getsentry/sentry PRs):
createANewDashboardForAnOrganizationwidgetseditAnOrganization_sCustomDashboardcreateANewReleaseForAnOrganizationprojectsupdateAnOrganization_sReleaseAfter each backend fix ships in a new
@sentry/apirelease, the corresponding CLI cast can be removed.Category 3 — Path parameter casts (3 casts, CLI-only, trivial)
Pattern:
dashboard_id: dashboardId as unknown as numberAlready blocked on
sentry#116836(dashboard_idtype fix). Once that ships:Wrapper types that need to stay (for now)
Three types keep fields beyond the SDK spec that have never been formally documented:
SentryOrganizationallowMemberProjectCreation,orgRoleSentryProjectstatus(undocumented API field)SentryEventdateCreated,fingerprints,sdkUpdates, typedcontextsWork order
unwrapResult<T>generic; drop response-type casts; simplify compatible wrapper types; replacedashboard_idcast withparseIntSuccess metric