jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.87k stars 2.78k forks source link

`FieldProps` type is not accepting proper generic type. #1998

Open jamil-alisgandarov opened 4 years ago

jamil-alisgandarov commented 4 years ago

🐛 Bug report

https://github.com/jaredpalmer/formik/blob/7b0752a73ad1676ed88e92ca49f2e7341cce4413/src/Field.tsx#L13

Current Behavior

FieldProps type accepts just one generic type where it uses same type both for field and form values. I think it is not correct behavior.

Expected behavior

Expected behavior is that FieldProps should accept 2 generic type.

Suggested solution(s)

It should accept 2 types, one for meta and field, another one for form key like below.

export interface FieldProps<V = any, FV = any> {
  field: FieldInputProps<V>;
  form: FormikProps<FV>;
  meta: FieldMetaProps<V>;
}
ridesz commented 4 years ago

Just an idea, but I think that something like this would be better:

export interface FieldProps<FORM_VALUE = any, FIELD_KEY extends keyof FORM_VALUE & string = any> {
    field: FieldInputProps<FORM_VALUE[FIELD_KEY]>;
    form: FormikProps<FORM_VALUE>;
    meta: FieldMetaProps<FORM_VALUE[FIELD_KEY]>;
}

After this I can see the proper types in my short test code:

const test1 = {
    a: "test",
    b: 42,
    c: true,
};

const fieldProps: FieldProps<typeof test1, "a"> = {} as any; // I just want to check the types...
const value = fieldProps.field; // Type of value: FieldInputProps<string>
const form = fieldProps.form; // Type of form: FormikProps<{ a: string; b: number; c: boolean; }>

Unfortunately this will not work with a deeper hierarchy:

const test2 = {
    a: "test",
    b: 42,
    c: {
        x: true, // You can not reference this with the typing above...
    },
};

But maybe this is possible with 3 generic variable:

export interface FieldProps<
    FORM_VALUE = any,
    FORM_VALUE_PART = any,
    FIELD_KEY extends keyof FORM_VALUE_PART & string = any
> {
    field: FieldInputProps<FORM_VALUE_PART[FIELD_KEY]>;
    form: FormikProps<FORM_VALUE>;
    meta: FieldMetaProps<FORM_VALUE_PART[FIELD_KEY]>;
}

Example 1:

const test2 = {
    a: "test",
    b: 42,
    c: {
        x: true,
    },
};

const fieldProps: FieldProps<typeof test2, typeof test2.c, "x"> = {} as any;
const value = fieldProps.field; // Type of value: FieldInputProps<boolean>
const form = fieldProps.form; // Type of form: FormikProps<{ a: string; b: number; c: { x: boolean; }; }>

Example 2:

const test3 = {
    a: "test",
    b: 42,
    c: {
        x: {
            y: 42,
        },
    },
};

const fieldProps: FieldProps<typeof test3, typeof test3.c.x, "y"> = {} as any;
const value = fieldProps.field; // Type of value: FieldInputProps<number>
const form = fieldProps.form; // Type of form: FormikProps<{ a: string; b: number; c: { x: { y: number; }; }; }>
Rangoric commented 4 years ago

FieldInputProps will also need a second parameter because otherwise the name field won't be of the right type.

Still working on a fix that I can use.

Rangoric commented 4 years ago

Current Fix that I am using that I still have to put through the paces:

interface IFixFieldInputProps<T, TBaseObject> extends Omit<FieldInputProps<T>, 'name'> {
  name: keyof TBaseObject;
}

export interface IFieldChildProps<TType, TBaseObject> {
  field: IFixFieldInputProps<TType, TBaseObject>;
  form: FormikProps<TBaseObject>;
}
Druotic commented 4 years ago

Unfortunately, this causes those using Typescript to lose all custom component field level form value type checking after upgrading to v2 :(

douweknook commented 4 years ago

I am also running into this problem. Why did the FieldProps type change actually? It seemed to work without any issues before v2. It inferred the type for field.value correctly from passing the field name and the form values types..

jkkjonah commented 4 years ago

Just wanted to add that this is still an issue in 2.1.5 and it also impacts FastFieldProps type