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
相关产品推荐
相关产品推荐

