TypeScript优雅实现未知对象转Person类型:冗余属性剔除与必填项校验
优雅实现TypeScript对象到Person类型的转换与校验
问题描述
需要编写一个TypeScript函数,接收任意结构的对象作为输入,返回严格符合Person类型的对象,满足以下要求:
- 自动剔除输入对象中
Person及嵌套Address类型未定义的额外属性 - 若输入缺失
Person或Address的必填属性,直接抛出错误 - 避免为每个属性编写大量
if判断,追求优雅可扩展的实现方式
给定类型定义:
interface Person { name: string; address: Address; } interface Address { street1: string; street2?: string; city: string; state: string; zip: string; phone?: string; }
解决方案:基于元数据的通用校验转换函数
核心思路是通过维护类型的元数据(必填属性、允许属性、嵌套类型映射),编写通用函数递归处理对象,避免重复的属性判断逻辑。
步骤1:定义类型元数据
先为每个类型定义对应的属性规则,后续校验和转换都基于这些元数据:
// 定义各类型的属性元数据,确保与接口定义一致 const typeMetadata = { Person: { required: ['name', 'address'] as const, allowed: ['name', 'address'] as const, nested: { address: 'Address' } }, Address: { required: ['street1', 'city', 'state', 'zip'] as const, allowed: ['street1', 'street2', 'city', 'state', 'zip', 'phone'] as const, nested: {} } } as const;
步骤2:编写通用校验转换函数
实现一个递归函数,根据元数据完成必填项校验、额外属性剔除和嵌套对象处理:
function validateAndTransform<T>( input: unknown, typeName: keyof typeof typeMetadata ): T { // 先判断输入是否为有效对象 if (typeof input !== 'object' || input === null) { throw new Error(`输入不是有效的对象,无法转换为${typeName}`); } const metadata = typeMetadata[typeName]; const result: Record<string, unknown> = {}; // 校验必填属性是否存在 for (const prop of metadata.required) { if (!(prop in input)) { throw new Error(`${typeName}类型缺失必填属性:${prop}`); } } // 保留允许的属性,并递归处理嵌套类型 for (const prop of metadata.allowed) { if (prop in input) { const nestedType = metadata.nested[prop as keyof typeof metadata.nested]; if (nestedType) { // 递归转换嵌套对象 result[prop] = validateAndTransform((input as Record<string, unknown>)[prop], nestedType); } else { result[prop] = (input as Record<string, unknown>)[prop]; } } } return result as T; }
步骤3:使用示例
// 示例1:输入包含额外属性,自动剔除 const validInput = { name: 'Alice', address: { street1: '123 Main St', city: 'New York', state: 'NY', zip: '10001', extraAddressProp: '无用属性' }, extraPersonProp: '无用属性' }; try { const person = validateAndTransform<Person>(validInput, 'Person'); console.log(person); // 输出:{ name: 'Alice', address: { street1: '123 Main St', city: 'New York', state: 'NY', zip: '10001' } } } catch (e) { console.error(e); } // 示例2:缺失Person必填属性,抛出错误 const invalidPersonInput = { address: { street1: '456 Oak Ave', city: 'Los Angeles', state: 'CA', zip: '90001' } }; try { validateAndTransform<Person>(invalidPersonInput, 'Person'); } catch (e) { console.error(e); // 输出:Person类型缺失必填属性:name } // 示例3:缺失Address必填属性,抛出错误 const invalidAddressInput = { name: 'Bob', address: { street1: '789 Pine Rd', city: 'Chicago', zip: '60601' } }; try { validateAndTransform<Person>(invalidAddressInput, 'Person'); } catch (e) { console.error(e); // 输出:Address类型缺失必填属性:state }
方案优势
- 避免冗余判断:无需为每个属性编写单独的
if校验,只需维护元数据对象 - 扩展性强:新增属性或嵌套类型时,仅需修改元数据,无需改动核心函数
- 递归处理嵌套:自动支持多层嵌套对象的校验与转换
- 类型安全:结合TypeScript泛型,确保返回值符合目标类型
内容的提问来源于stack exchange,提问作者yumba
相关产品推荐
相关产品推荐

