T
TanStack2mo ago
conscious-sapphire

tanstack form type errors with a zod discriminated union

For context, here is my schema expressed in ts types for clarity
{
engine_type: "Petrol" | "Petrol-LPG" | "Diesel" | "Mild Hybrid" | "Hybrid" | "Plug-in Hybrid";
manufacturer: string;
model: string;
variant: string;
price: number;
co2_min?: number | null | undefined;
co2_max?: number | null | undefined;
engine_displacement?: number | null | undefined;
fuel_economy_min?: number | null | undefined;
fuel_economy_max?: number | null | undefined;
hp?: number | null | undefined;
url?: string | null | undefined;
} | {
engine_type: "Electric";
manufacturer: string;
model: string;
variant: string;
price: number;
battery_capacity?: number | null | undefined;
energy_consumption?: number | ... 1 more ... | undefined;
battery_range?: number | ... 1 more ... | undefined;
hp?: number | ... 1 more ... | undefined;
url?: string | ... 1 more ... | undefined;
}
{
engine_type: "Petrol" | "Petrol-LPG" | "Diesel" | "Mild Hybrid" | "Hybrid" | "Plug-in Hybrid";
manufacturer: string;
model: string;
variant: string;
price: number;
co2_min?: number | null | undefined;
co2_max?: number | null | undefined;
engine_displacement?: number | null | undefined;
fuel_economy_min?: number | null | undefined;
fuel_economy_max?: number | null | undefined;
hp?: number | null | undefined;
url?: string | null | undefined;
} | {
engine_type: "Electric";
manufacturer: string;
model: string;
variant: string;
price: number;
battery_capacity?: number | null | undefined;
energy_consumption?: number | ... 1 more ... | undefined;
battery_range?: number | ... 1 more ... | undefined;
hp?: number | ... 1 more ... | undefined;
url?: string | ... 1 more ... | undefined;
}
In my form the default value of engine_type is 'Petrol", so the 1st variant is used for type checking. I am using the form.Subscribe HOC to show only the relavant fields based on the engine_type. The type error manifests in the electric car only fields. Any idea how to get rid of the type error without casting?
4 Replies
exotic-emerald
exotic-emerald2mo ago
how do you give the form the default values? what might be happening is two things: 1. You're giving the defaultValues directly. TypeScript won't know it can be a union in this case, so it will complain. 2. You're assigning defaultValues externally with the correct type. However, TypeScript thinks that, since defaultValues is never reassigned, it is only part of the union. It's a problem that has been around for a while, and neither satisfies or as can properly solve it.
type Animal = Dog | Cat;

const value: Animal = cat();
// ^? Cat

// this also won't do, not type safe
const value = cat() as Animal;

// this doesn't change the type
const value = cat() satisfies Animal;
// ^? Cat

// this does what you expect
const value = cat() satisfies Animal as Animal;

// Alternative approach
function identity<T>(obj: T): T { return T; }

const value = identity<Animal>(cat())
type Animal = Dog | Cat;

const value: Animal = cat();
// ^? Cat

// this also won't do, not type safe
const value = cat() as Animal;

// this doesn't change the type
const value = cat() satisfies Animal;
// ^? Cat

// this does what you expect
const value = cat() satisfies Animal as Animal;

// Alternative approach
function identity<T>(obj: T): T { return T; }

const value = identity<Animal>(cat())
conscious-sapphire
conscious-sapphireOP2mo ago
I use the 2nd approach, external assignment Satisfies + as combo works 🙂
exotic-emerald
exotic-emerald2mo ago
for a quick check, satisfies X as X should "solve the types" okay, yeah then it was that issue as general note, you'll often see this problem pop up with string unions and assigning an initial one. An identity function never hurts to be around for type safety An example from our workplace:
export function oneof<T = never>(value: T): T { return T; }
export function oneof<T = never>(value: T): T { return T; }
exotic-emerald
exotic-emerald2mo ago
Also the TypeScript repository's issue talking about this: https://github.com/microsoft/TypeScript/issues/61789
GitHub
Skip assignment narrowing when declaring & initializing a variable ...
🔍 Search Terms declare, assign, declaration, assignment, narrow, narrowing, union, variable, &quot;control flow analysis&quot; ✅ Viability Checklist This wouldn&#39;t be a breaking change in existi...

Did you find this page helpful?