-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Which project does this relate to?
Start
Describe the bug
When using z.discriminatedUnion with z.looseObject() (the Zod v4 recommended replacement for z.object().passthrough()) as the inputValidator for createServerFn, TypeScript reports type errors because the inferred type includes [x: string]: unknown, which is not assignable to what createServerFn internally expects ([x: string]: {}).
Minimal Reproduction
import { z } from 'zod';
import { createServerFn } from '@tanstack/react-start';
const storageSettingsSchema = z.discriminatedUnion('provider', [
z.looseObject({ provider: z.literal('local') }),
z.looseObject({
provider: z.literal('s3'),
s3: z.object({
endpoint: z.string().min(1),
bucket: z.string().min(1),
}),
}),
]);
type StorageSettings = z.infer<typeof storageSettingsSchema>;
// Inferred type includes: { [x: string]: unknown; provider: "local" } | ...
// ❌ TS2345: Type 'Promise<{ [x: string]: unknown; ... }>'
// is not assignable to type 'Promise<{ [x: string]: {}; ... }>'
export const getSettings = createServerFn({ method: 'GET' })
.handler(async (): Promise<StorageSettings> => {
return { provider: 'local' };
});
// ❌ Same error on inputValidator + handler return
export const updateSettings = createServerFn({ method: 'POST' })
.inputValidator(storageSettingsSchema)
.handler(async ({ data }): Promise<StorageSettings> => {
return data;
});Context
In Zod v4, z.looseObject() is the recommended way to allow extra keys to pass through (replacing the legacy .passthrough() method). It infers [x: string]: unknown by design. This is semantically correct since extra keys could hold any value including null or undefined.
The root cause appears to be a restrictive type constraint inside createServerFn's generics that narrows unknown → {}, which excludes null | undefined from the index signature's value type.
Your Example Website or App
https://stackblitz.com/edit/github-geldcamw?file=src%2Froutes%2Findex.tsx
Expected behavior
createServerFn should accept return types / input types with [x: string]: unknown index signatures, since this is what Zod v4's z.looseObject() infers.
Actual Behavior
TypeScript reports errors because somewhere in createServerFn's internal type chain, the index signature is constrained to [x: string]: {}, and unknown is not assignable to {}.
Screenshots or Videos
No response
Platform
@tanstack/react-start: 1.159.5@tanstack/react-router: 1.159.5zod: 4.3.6typescript: 5.9.3
Additional context
No response