TypeScript遍历嵌套对象提取含required键的对象键时的类型错误解决
问题描述
我定义了如下TypeScript类型:
export type DataType = { id: string; type: string; 'product-configuration': { inputs: ConfigurationType }; }; export type ConfigurationType = { [key: string]: ItemDataType; }; export type ItemDataType = { required: boolean; type: string; hint?: string; constraint?: ConstraintType; options?: string[]; conditional?: ConditionalItemDataType; }; export type ConstraintType = { min?: number; max?: number; format?: string; }; export type ConditionalItemType = [ { condition: string }, { [key: string]: ItemDataType } ]; export type ConditionalItemDataType = ConditionalItemType[];
同时有一个符合该类型的对象:
const abc = { id: 45, type: 'product', 'product-configuration': { inputs: { 'Instance Name': { required: true, type: 'text', constraint: { min: 3, max: 15, format: 'alpha-hyphen-numeric', }, }, Environment: { required: true, type: 'select', options: ['Prod', 'Dev'], }, 'Content Type': { required: true, type: 'select', options: ['Markdown', 'HTML'], conditional: [ [ { condition: 'Markdown' }, { 'Folder Location': { required: true, type: 'text', constraint: { max: 30, }, }, }, ], ], }, 'Content folder path': { required: false, type: 'select', options: ['Root', 'Content'], }, 'Start Date': { required: true, type: 'date', }, 'Need Playground': { required: false, type: 'select', options: ['Yes', 'No'], }, }, }, };
我想要提取所有包含required键的对象的键名,为此编写了如下代码,该代码在JavaScript中运行正常,但在TypeScript中出现类型错误。我通过将typeof obj[key] === 'object'修改为typeof obj[key as keyof DataType] === 'object'消除了第一个错误,但无法解决另外两个错误。若使用obj: any可消除前两个错误,但第三个错误仍存在,且我不想使用any。请问如何在不使用any的情况下解决这些类型错误?
原代码:
const findFieldNames = (obj, result = [], currentPath = '') => { for (const key in obj) { const newPath = currentPath ? `${currentPath}.${key}` : key; if (typeof obj[key] === 'object') { findFieldNames(obj[key], result, newPath); } else if (key === 'required') { result.push(newPath.match(/(?<=\.)([^.]+)(?=\.required$)/)[0]); } } return result; }
解决方案
1. 定义覆盖所有遍历场景的联合类型
首先,我们需要明确函数要处理的所有可能结构,定义一个联合类型来替代any:
type TraversableType = | DataType | ConfigurationType | ItemDataType | ConstraintType | ConditionalItemDataType | ConditionalItemType | { [key: string]: ItemDataType };
2. 修正类型安全的遍历逻辑
- 使用
keyof typeof obj确保遍历的键合法 - 添加
value !== null判断,因为typeof null也会返回object,但我们不需要遍历null - 处理正则匹配的空值风险,避免直接访问
match[0]导致的报错
最终修改后的代码
type TraversableType = | DataType | ConfigurationType | ItemDataType | ConstraintType | ConditionalItemDataType | ConditionalItemType | { [key: string]: ItemDataType }; const findFieldNames = (obj: TraversableType, result: string[] = [], currentPath = '') => { for (const key in obj) { const value = obj[key as keyof typeof obj]; const newPath = currentPath ? `${currentPath}.${key}` : key; // 仅遍历非空的对象或数组 if (typeof value === 'object' && value !== null) { findFieldNames(value as TraversableType, result, newPath); } else if (key === 'required') { const match = newPath.match(/(?<=\.)([^.]+)(?=\.required$)/); // 确保匹配成功再提取结果 if (match) { result.push(match[0]); } } } return result; };
关键说明
- 联合类型
TraversableType:覆盖了所有需要递归遍历的结构,让TypeScript能识别参数的合法范围,避免any的滥用 - 空值过滤:排除
null,避免无效的递归调用 - 正则匹配安全处理:先检查匹配结果是否存在,解决TypeScript对可能
null值的报错,同时提升代码健壮性
测试调用示例:
const requiredFields = findFieldNames(abc as DataType); console.log(requiredFields); // 输出: ["Instance Name", "Environment", "Content Type", "Folder Location", "Start Date"]
内容的提问来源于stack exchange,提问作者moys
相关产品推荐
相关产品推荐

