TypeScript中如何检查对象是否符合指定类型(含复合类型)?
在TypeScript中优雅区分两种数据类型的方案
要避免通过属性名硬判断,同时让TypeScript能正确做类型推断,核心思路是利用类型守卫(Type Guards)或判别式联合类型(Discriminated Unions),下面给你两种实用的方案:
方案一:用类型守卫区分基础类型与复合类型
如果你的「第一种类型」是值为基础类型(string/number/boolean)的对象,「第二种复合类型」是包含嵌套对象/数组的结构,可以按以下步骤实现:
1. 先定义类型
// 第一种:值全为基础类型的简单对象 type SimpleObject = Record<string, string | number | boolean>; // 第二种:包含嵌套对象/数组的复合类型 type ComplexObject = Record<string, SimpleObject | ComplexObject | (SimpleObject | ComplexObject)[]>;
2. 编写类型守卫函数
类型守卫能在运行时判断类型,同时让TypeScript识别对应的类型:
// 判断值是否为基础类型 function isPrimitive(value: unknown): value is string | number | boolean { return ['string', 'number', 'boolean'].includes(typeof value); } // 判断对象是否为SimpleObject function isSimpleObject(obj: unknown): obj is SimpleObject { if (typeof obj !== 'object' || obj === null) return false; // 验证所有属性值都是基础类型 return Object.values(obj).every(isPrimitive); } // 判断对象是否为ComplexObject function isComplexObject(obj: unknown): obj is ComplexObject { if (typeof obj !== 'object' || obj === null) return false; // 只要有一个属性值是复合结构即可判定 return Object.values(obj).some(value => { if (Array.isArray(value)) { return value.some(item => isSimpleObject(item) || isComplexObject(item)); } return isSimpleObject(value) || isComplexObject(value); }); }
3. 遍历并处理数据
借助类型守卫,遍历时代码会自动获得类型提示:
function handleData(data: SimpleObject | ComplexObject) { if (isSimpleObject(data)) { console.log('处理第一种简单类型对象:'); for (const [key, value] of Object.entries(data)) { // TypeScript已推断value为基础类型,可直接安全处理 console.log(`${key}: ${value}`); } } else { console.log('处理第二种复合类型对象:'); for (const [key, value] of Object.entries(data)) { // 递归处理嵌套的复合结构 if (Array.isArray(value)) { value.forEach(item => handleData(item)); } else { handleData(value); } } } }
方案二:用判别式联合类型(场景化最优解)
如果你的两种类型可以新增一个标识字段(比如type),这种方案会更简洁,TypeScript会自动帮你做类型窄化:
1. 定义带判别字段的联合类型
// 第一种类型:带type标识的简单对象 type SimpleItem = { type: 'simple'; name: string; age: number; isActive: boolean; }; // 第二种复合类型:带type标识的嵌套结构 type ComplexItem = { type: 'complex'; children: (SimpleItem | ComplexItem)[]; category: string; }; // 联合类型 type DataItem = SimpleItem | ComplexItem;
2. 遍历并处理数据
直接通过type字段区分类型,无需额外守卫:
function processItem(item: DataItem) { for (const [key, value] of Object.entries(item)) { if (item.type === 'simple') { // TypeScript自动将item窄化为SimpleItem,所有属性值都是基础类型 console.log(`简单属性 ${key}: ${value}`); } else { // TypeScript自动将item窄化为ComplexItem if (key === 'children') { // 递归处理子项数组 (value as DataItem[]).forEach(processItem); } else { console.log(`复合属性 ${key}: ${value}`); } } } }
为什么这两种方案更优雅?
- 避免硬编码属性名的检查,属性名变更时只需修改类型定义,无需到处调整判断逻辑
- TypeScript能正确推断类型,减少
any类型的使用,提升代码安全性 - 逻辑封装性好,类型判断逻辑集中在守卫函数或判别字段中,遍历代码更简洁
内容的提问来源于stack exchange,提问作者Mustafa Magdy
相关产品推荐
相关产品推荐

