tRPC v10 Zod‑auto‑inference: Uncertain Handling of Optional Fields in Auto‑Inferred Outputs
0 reputation · 24 Aug 2021, 11:11 UTC
0 reputation · 24 Aug 2021, 11:11 UTC
We want to rely on tRPC v10’s Zod‑auto‑inference to provide fully typed end‑to‑end calls without manual type annotations, especially when procedures return objects that contain optional properties or default values defined in the Zod schema.
The auto‑inferred output type is generated from the Zod schema used for the procedure’s return value. It is unclear whether optional keys that are omitted in the actual runtime response are preserved in the inferred TypeScript type, and whether default values specified in the schema are reflected in the inferred type or only at runtime.
33775 reputation · 24 Aug 2021, 11:59 UTC
In tRPC v10, Zod-auto-inference preserves all keys defined in the Zod schema within the resulting TypeScript type, regardless of whether those keys are present in the actual runtime response. If a field is marked as .optional(), the inferred type will include that key as optional (e.g., key?: T | undefined). This remains true even if the server-side procedure omits the key entirely from the return object.
There is a critical distinction between Zod's .default() behavior and TypeScript's type inference:
Because tRPC's auto-inference only provides type-level hints and does not transform the runtime JSON payload, the client may receive an object missing keys that the type says are present. To guarantee the client receives the full shape, use one of the following patterns:
undefined to those that are absent..parse() method before returning it from the procedure. This forces Zod to apply default values and structure the object according to the schema.You can verify this behavior by defining a schema and inspecting the output:
const schema = z.object({
required: z.string(),
optionalField: z.string().optional(),
defaultField: z.number().default(5),
});
optionalField?: string | undefined and defaultField: number.{ required: "val" }, the defaultField will be missing from the JSON, despite the type claiming it is a required number.Diagnostic Note: Are you using a custom transformer (like superjson)? Transformers can affect how undefined and null are serialized, which may change how missing keys are handled during the transport layer.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.