`FormAtomValues` hints wrong type to the developer
MiroslavPetrik opened this issue Β· comments
Describe the bug
The return type of FormAtomValues
should contain only primitive language types, while it still mentions FieldAtom
generics.
The type itself is correct, but the developer experience could be better.
Case 1: array of field atoms FieldAtom<T>[]
const imageFields = {
images: [
fieldAtom({
value: { id: "1", url: "https://" },
}),
fieldAtom({
value: { id: "2", url: "https://" },
}),
],
};
// when hovered/inspected
type ImageValuesACTUAL = FormAtomValues<typeof imageFields>;
// ISSUE1: the [x: number] is ugly way to say there is array of items
// ISSUE2: FieldAtom is obfuscating the actual type
// ISSUE3: there is recursive call to FormAtomValues
type ImageValuesACTUAL = {
images: FormAtomValues<{
[x: number]: FieldAtom<{
id: string;
url: string;
}>;
}>;
};
// FIX1: the array has intuitive syntax []
// FIX2: we have plain values, not boxes of FieldAtom
// FIX3: no recursive call
type ImageValuesEXPECTED = {
images: {
id: string;
url: string;
}[];
};
// Fix of https://github.com/jaredLunde/form-atoms/blob/main/src/index.tsx#L1208
type _FormAtomValues<Fields extends FormAtomFields> = {
[Key in keyof Fields]: Fields[Key] extends FieldAtom<infer Value>
? Value
: Fields[Key] extends FormAtomFields
? _FormAtomValues<Fields[Key]>
: Fields[Key] extends FieldAtom<infer Value>[] // FIX2: here go deeper into array of FieldAtom; no need to recurr
? Value[]
: Fields[Key] extends FormAtomFields[] // FIX1: fixes the [x: number] for Case 2 below
? _FormAtomValues<FormAtomFields>
: never;
};
Case 2: array of 'fields' {[string]: FieldAtom<any>}[]
const addressesFields = {
addresses: [
{
city: fieldAtom({ value: "Stockholm" }),
street: fieldAtom({ value: "Carl Gustav Street" }),
},
{
city: fieldAtom({ value: "Bratislava" }),
street: fieldAtom({ value: "Kosicka" }),
},
],
};
type AddressValuesACTUAL = FormAtomValues<typeof addressesFields>;
// when hovered
type AddressValuesACTUAL = {
addresses: FormAtomValues<{
[x: number]: {
city: FieldAtom<string>;
street: FieldAtom<string>;
};
}>;
}
type AddressValuesEXPECTED = {
addresses: {
city: string;
street: string;
}[];
}
// Now the fix is a bit trickier - the arrays does not contain leaf nodes, they have objects, so we must recurr,
// but recurr would keep the `FormAtomValues` call indication in the result, and obfuscate it
// SO WE CAN recurr by duplicating the code.
// This does not cover all cases, e.g. deeply nested form definitions would be still 'bad', but certain level, e.g. depth 2
// could be supported to cover most user needs:
type _FormAtomValues<Fields extends FormAtomFields> = {
[Key in keyof Fields]: Fields[Key] extends FieldAtom<infer Value>
? Value
: Fields[Key] extends FormAtomFields
? _FormAtomValues<Fields[Key]>
: Fields[Key] extends FieldAtom<infer Value>[]
? Value[]
: Fields[Key] extends (infer NestedFields)[]
? NestedFields extends FormAtomFields
? {
[K2 in keyof NestedFields]: NestedFields[K2] extends FieldAtom<
infer Value
>
? Value
: NestedFields[K2] extends FormAtomFields
? _FormAtomValues<NestedFields[K2]>
: NestedFields[K2] extends any[]
? _FormAtomValues<NestedFields[K2][number]>
: never;
}[]
: never
: never;
};
Additional context
- the same should be done for
FormAtomErrors
andFormAtomTouchedFields
- when the recursive call will start only from level 1 or 2, we should manually unwrap also the 1 level nesting on line https://github.com/jaredLunde/form-atoms/blob/main/src/index.tsx#L1207
I prefer the current type as it is more readable and thus easier to maintain.
Am I missing something? Wouldn't this work nearly as well while remaining readable? I get that it doesn't display to the developer perfectly, but it is a slight improvement that type checks correctly which is the important thing.
export type FormFieldValues<Fields extends FormFields> = {
[Key in keyof Fields]: Fields[Key] extends FieldAtom<infer Value>
? Value
: Fields[Key] extends FormFields
? FormFieldValues<Fields[Key]>
: Fields[Key] extends Array<infer Item>
? Item extends FieldAtom<infer Value>
? Value[]
: Item extends FormFields
? FormFieldValues<Item>[]
: never
: never;
};
π This issue has been resolved in version 2.0.0-next.1 π
The release is available on:
Your semantic-release bot π¦π
@jaredLunde Yes, the version from case 1 will be ok, as it is an improvement.
I don't like the readability of the _FormAtomValues
from case 2 neither, it was only illustration that its possible to achieve :)
Maybe typescript will improve this automatically in the future, by eagerly resolving the 'generit type calls' instead of displaying them. Who knows.
π This issue has been resolved in version 2.0.0 π
The release is available on:
Your semantic-release bot π¦π