You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用TypeScript泛型正确关联函数参数?优化makeValidate类型

优化表单验证工具函数的泛型类型定义,消除冗余断言

想用happyValidate替代手写的sadValidate,但无法正确实现makeValidate的泛型类型定义。目前通过添加大量as断言能让代码运行,但希望优化类型标注,去掉这些冗余断言。

原始代码示例

type ValidationFn = (s: string) => boolean;

interface Validation {
  check: ValidationFn;
  message: string;
}

const validIdentityNameCharacters = /^[a-zA-Z0-9_.-\s]*$/;
const hasSpecialCharacters: Validation = {
  check: s => !validIdentityNameCharacters.test(s),
  message: 'Must only include letters, numbers and the following special characters: (_ -.)',
};

const LIMIT = 255;
const isLengthTooLong: Validation = {
  check: s => s.length >= LIMIT,
  message: `Must be less than ${LIMIT} characters`,
};

const isBlank: Validation = { check: s => s === '', message: 'Must not be blank' };

interface Values {
  name: string;
  tags: string;
}

const sadValidate = (values: Values): Partial<Values> => {
  const errors: Partial<Values> = {};
  const { name, tags } = values;

  if (hasSpecialCharacters.check(name)) {
    errors.name = hasSpecialCharacters.message;
  } else if (isLengthTooLong.check(name)) {
    errors.name = isLengthTooLong.message;
  } else if (isBlank.check(name)) {
    errors.name = isBlank.message;
  }

  if (isLengthTooLong.check(tags)) {
    errors.tags = isLengthTooLong.message;
  }

  return errors;
};

const makeValidate = config => values =>
  Object.keys(config).reduce((acc, fieldName) => {
    const validators = config[fieldName];
    const problem = validators.find(({ check }) => check(values[fieldName]));
    if (problem) {
      acc[fieldName] = problem.message;
    }
    return acc;
  }, {});

const happyValidate = makeValidate({
  name: [hasSpecialCharacters, isLengthTooLong, isBlank],
  tags: [isLengthTooLong],
});

test('works', () => {
  expect(happyValidate({ name: '', tags: new Array(260).fill('a').join('') })).toEqual({
    name: isBlank.message,
    tags: isLengthTooLong.message,
  });
});

当前带大量as断言的可行实现

export const makeValidate =
  <V>(config: Record<keyof V, Validation[]>) =>
  (values: V): Record<keyof V, string> =>
    Object.keys(config).reduce<Record<keyof V, string>>((acc, fieldName) => {
      const validators = config[fieldName as keyof V];
      const problem = validators.find(({ check }) => check(values[fieldName as keyof V] as string));
      if (problem) {
        acc[fieldName as keyof V] = problem.message;
      }
      return acc;
    }, {} as Record<keyof V, string>);

内容的提问来源于stack exchange,提问作者mholm815

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 09:07:46