Ограничить значения объекта скалярами

Я использую Formik, в котором есть этот тип:

type FormikErrors<Values> = {
 [K in keyof Values]?: Values[K] extends object
  ? FormikErrors<Values[K]>
  : string
};

Затем есть функция проверки, которая в основном выглядит как validate<Values>(v: Values) => FormikErrors<Values>. Идея состоит в том, что ключи FormikErrors совпадают с ключами Values ​​и сопоставляются со строковым сообщением об ошибке ИЛИ рекурсивным объектом FormikErrors, если это поле представлено вложенным объектом.

Я пытаюсь написать общую функцию для проверки обязательных полей. Он должен работать только с плоскими значениями.

export function validateRequired<T, K extends keyof T>(values : T, names: K[]) : FormikErrors<T> {
 let errors : FormikErrors<T> = {};
 names.forEach((name) => {
  if (!values[name]) {
   errors[name] = 'This field is required';
  }
 });
 return errors;
}

Хотя это ошибка:

Type error: Type '"This field is required"' is not assignable to type '(T[K] extends object ? FormikErrors<T[K]> : string) | undefined'. TS2322

Поскольку значения объекта, возвращаемые validateRequired, всегда являются строками, а не вложенными значениями FormikValues. Есть ли способ указать, что значения всегда будут скалярами, чтобы можно было ввести проверку?


person Gavin Wahl    schedule 11.04.2019    source источник


Ответы (1)


Простым решением было бы не использовать тип FormikErrors. Используйте настраиваемый тип, который может иметь только строковые значения:

type RequiredErrors<Values> = {
    [K in keyof Values]?: string
};

export function validateRequired<T, K extends keyof T>(values: T, names: K[]): RequiredErrors<T> {
    let errors: RequiredErrors<T> = {};
    names.forEach((name) => {
        if (!values[name]) {
            errors[name] = 'This field is required';
        }
    });
    return errors;
}

Если это не вариант для вас, то единственным решением является утверждение типа. Typescript не может разрешать условные типы, в которых все еще есть неразрешенные параметры универсального типа, и, следовательно, не может действительно сказать, является ли string допустимым значением для объекта errors, но утверждение типа исправит это:

export function validateRequired<T, K extends keyof T>(values: T, names: K[]): FormikErrors<T> {
    let errors: FormikErrors<T> = {};
    names.forEach((name) => {
        if (!values[name]) {
            errors[name] = 'This field is required' as any;;
        }
    });
    return errors;
}
person Titian Cernicova-Dragomir    schedule 11.04.2019
comment
Я думал, что мне нужно использовать FormikErrors (это из библиотеки), но на самом деле он работает, чтобы заставить validateRequired возвращать RequiredErrors, а затем фактическая функция проверки не является универсальной, поэтому все работает. - person Gavin Wahl; 12.04.2019