将对象的值限制为标量

时间:2019-04-11 20:36:29

标签: typescript typescript-generics

我正在使用其中具有这种类型的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。有没有一种方法可以指定值始终是标量,以便可以键入check?

1 个答案:

答案 0 :(得分: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;
}