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

TypeScript:不使用Any/Unknown从函数参数创建任意类型对象

问题分析

你遇到的编译错误,核心原因是泛型U没有任何类型约束——编译器无法保证U是一个对象类型(它可能是number、boolean等非对象类型),更无法确定空对象{}是否符合U的结构要求(比如你的LocationFormErrors是带有必填字符串属性的对象,空对象显然不满足)。

下面提供两种无需any/unknown双重断言的规范解决方案:


方案1:给U添加对象类型约束(适用于允许属性可选的错误类型)

通过约束U为属性值是字符串或undefined的对象,让编译器认可空对象是U的合法子类型:

首先补全ValidationMap的类型定义(假设你之前没定义):

type ValidationRule = {
  length?: [number, number];
};
type ValidationMap = Record<string, ValidationRule>;

修改泛型函数:

export const validateFormObject = <T, U extends Record<string, string | undefined>>(
  formObject: T,
  validationMap: ValidationMap
): U => {
  // 空对象符合约束,用as U断言安全
  let errors: U = {} as U;

  // 示例验证逻辑:遍历规则并赋值错误信息
  for (const [field, rules] of Object.entries(validationMap)) {
    const fieldKey = field as keyof T & keyof U;
    const value = formObject[fieldKey] as string;

    if (rules.length) {
      const [min, max] = rules.length;
      if (!value || value.length < min || value.length > max) {
        errors[fieldKey] = `长度需在${min}-${max}字符之间`;
      }
    }
  }

  return errors;
};

方案2:初始化完整结构的错误对象(适用于必填属性的错误类型)

如果你的错误类型(比如LocationFormErrors)要求所有属性必须存在且为字符串,可以通过遍历表单字段生成初始值(空字符串代表无错误),避免空对象不匹配的问题:

type ValidationRule = {
  length?: [number, number];
};
type ValidationMap = Record<string, ValidationRule>;

// 约束U与表单T的键对应,且所有属性为必填字符串
export const validateFormObject = <T, U extends Record<keyof T, string>>(
  formObject: T,
  validationMap: ValidationMap
): U => {
  // 生成包含所有表单字段、初始值为空字符串的对象
  const initialErrors = Object.fromEntries(
    Object.keys(formObject).map(key => [key, ""])
  ) as U;

  let errors = initialErrors;

  // 示例验证逻辑
  for (const [field, rules] of Object.entries(validationMap)) {
    const fieldKey = field as keyof T & keyof U;
    const value = formObject[fieldKey] as string;

    if (rules.length) {
      const [min, max] = rules.length;
      if (value.length < min || value.length > max) {
        errors[fieldKey] = `长度必须在${min}到${max}之间`;
      }
    }
  }

  return errors;
};

额外优化:让类型自动推导(简化代码)

如果你的错误类型总是和表单字段一一对应(每个字段对应一个字符串错误),可以省略显式指定U,让编译器自动推导返回类型:

export const validateFormObject = <T>(
  formObject: T,
  validationMap: ValidationMap
): Record<keyof T, string> => {
  const initialErrors = Object.fromEntries(
    Object.keys(formObject).map(key => [key, ""])
  ) as Record<keyof T, string>;

  // 验证逻辑...

  return initialErrors;
};

// 使用时无需手动指定泛型
const validate = (location: Location) => {
  let errors = validateFormObject(location, {
    name: { length: [3, 20] },
    description: { length: [3, 20] },
  });
  return errors;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 13:48:25