You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #2083. Landed in that PR: 100% removal of axios, full Treaty migration, and a single source-of-truth server type graph. What remains is a class of 28 `as unknown as LocalType` casts at the hook boundary in `apps/expo`, all caused by one root issue.
Root cause
Treaty infers Elysia's response types from the Zod schemas. Where schemas reflect Drizzle column types directly (e.g. `createdAt: z.date()`, `price: z.number().nullable()`), the inferred wire type is `Date` / `number | null`. But the JSON actually sent over the wire serializes Dates to ISO strings. The client consumers (UI components, date formatters, comparisons) are written against the wire reality — `string` / `string | null`.
The mismatch: Treaty types the response as `{ createdAt: Date }`, the actual payload is `{ createdAt: "2026-04-14T..." }`, consumers expect `string`. The 28 casts paper over this at the hook boundary.
Scope
Full fix: wrap every datetime / server-specific column in its response schema with `.transform()` so the wire type matches what consumers already consume.
Affected server schemas — every Zod schema used as a route `response` that mirrors a Drizzle row with Date columns:
For each response schema, identify every Date field and wrap with `.transform(d => d.toISOString())`.
For nullable Dates, wrap with `.nullable().transform(d => d?.toISOString() ?? null)`.
Replace each local type (`Pack`, `PackItem`, `CatalogItem`, `Trip`, etc.) with the Treaty-inferred equivalent derived from `apiClient`:
```ts
type PackListResponse = NonNullable<Awaited<ReturnType>['data']>;
export type Pack = PackListResponse[number];
```
Remove each `as unknown as X` cast. Fix any consumer errors that surface.
Verify:
Root `tsc --noEmit` clean
No `as unknown as` in `apps/expo/features`
No new `biome-ignore` or `@ts-expect-error`
Done when
All response schemas transform dates to ISO strings at the wire boundary
Local domain types (`Pack`, `PackItem`, `CatalogItem`, `Trip`, `Post`, `Comment`, `PackTemplate`, `PackTemplateItem`, `TrailConditionReport`) are derived from Treaty
Zero `as unknown as` casts in `apps/expo/features` and `apps/expo/lib/api`
`bun run check-types` and `bun run lint` both green
No new escape hatches (`any`, `@ts-expect-error`, biome-ignore)
#2083 is at ~150 files touched and merges cleanly today. This cleanup touches a different dimension (server schemas + client type consumers, ~60 files) and benefits from isolated review. Should base on #2083's branch so it merges after.
Follow-up to #2083. Landed in that PR: 100% removal of axios, full Treaty migration, and a single source-of-truth server type graph. What remains is a class of 28 `as unknown as LocalType` casts at the hook boundary in `apps/expo`, all caused by one root issue.
Root cause
Treaty infers Elysia's response types from the Zod schemas. Where schemas reflect Drizzle column types directly (e.g. `createdAt: z.date()`, `price: z.number().nullable()`), the inferred wire type is `Date` / `number | null`. But the JSON actually sent over the wire serializes Dates to ISO strings. The client consumers (UI components, date formatters, comparisons) are written against the wire reality — `string` / `string | null`.
The mismatch: Treaty types the response as `{ createdAt: Date }`, the actual payload is `{ createdAt: "2026-04-14T..." }`, consumers expect `string`. The 28 casts paper over this at the hook boundary.
Scope
Full fix: wrap every datetime / server-specific column in its response schema with `.transform()` so the wire type matches what consumers already consume.
Affected server schemas — every Zod schema used as a route `response` that mirrors a Drizzle row with Date columns:
Typical transform pattern:
```ts
createdAt: z.date().transform((d) => d.toISOString()),
updatedAt: z.date().transform((d) => d.toISOString()),
expiresAt: z.date().nullable().transform((d) => d?.toISOString() ?? null),
```
Affected client files — 24 hook/store files with `as unknown as` casts that should be removed once the server types align:
```
apps/expo/features/ai-packs/hooks/useGeneratedPacks.ts
apps/expo/features/ai/hooks/useReportContent.ts
apps/expo/features/ai/hooks/useReportedContent.ts
apps/expo/features/ai/hooks/useUpdateReportStatus.ts
apps/expo/features/catalog/hooks/useCatalogItems.ts
apps/expo/features/catalog/hooks/useCatalogItemsCategories.ts
apps/expo/features/catalog/hooks/useSimilarItems.ts
apps/expo/features/pack-templates/hooks/useGenerateTemplateFromOnlineContent.ts
apps/expo/features/pack-templates/store/packTemplateItems.ts
apps/expo/features/packs/hooks/useAllPacks.ts
apps/expo/features/packs/hooks/useDuplicatePack.ts
apps/expo/features/packs/hooks/useImageDetection.ts
apps/expo/features/packs/hooks/usePackDetailsFromApi.ts
apps/expo/features/packs/hooks/usePackGapAnalysis.ts
apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts
apps/expo/features/packs/hooks/useSeasonSuggestions.ts
apps/expo/features/packs/store/packItems.ts
apps/expo/features/packs/utils/uploadImage.ts
apps/expo/features/trail-conditions/hooks/useTrailConditionReports.ts
apps/expo/features/trips/hooks/useAllTrips.ts
apps/expo/features/weather/hooks/useWeatherAlert.ts
apps/expo/features/weather/lib/weatherService.ts
apps/expo/features/wildlife/hooks/useWildlifeIdentification.ts
```
Approach
```ts
type PackListResponse = NonNullable<Awaited<ReturnType>['data']>;
export type Pack = PackListResponse[number];
```
Done when
Why separate from #2083
#2083 is at ~150 files touched and merges cleanly today. This cleanup touches a different dimension (server schemas + client type consumers, ~60 files) and benefits from isolated review. Should base on #2083's branch so it merges after.