在客户端开发中,你是否曾遇到过这样的困扰:一次看似寻常的网络数据解析,却导致了出人意料的崩溃;一个本该正常的文件读取操作,却返回了难以理解的数据错误。这些问题的根源,往往指向同一环节——数据类型转换。当应用面对网络传输、文件I/O等不可控的数据源时,如何稳健、准确地进行数据解析与转换,就成为保障应用稳定性的第一道防线。
本篇内容是《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》这本书第四章内容的延续,是咱这本书读者的福利,在本篇内容中以模拟多种数据输入,向复杂类型(类、数组、Record)数据转换的输出,以确定最优的代码编方式,以避免非预期的异常产生,每个示例可以独立的编译及调试,欢迎大家一同来深入的解,甚至可以当作面试题来学习。
建议读者可以先看一下:
【鸿蒙生态共建】一文说清基础类型数据的非预期输入转换与兜底-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
打个广告,对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)
往期福利:
- 【页面路由导航】三步实现页面跳转的完整示例-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
- 【鸿蒙生态共建】一文10个示例讲懂鸿蒙系统ArkTS中的null与undefined的区别与应用-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
- 【鸿蒙生态共建】一文两表三实例讲清async函数与普通函数的区别-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
- 【鸿蒙生态共建】一文说清基础类型数据的非预期输入转换与兜底-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
- 【鸿蒙生态共建】一文说明兼容版本、目标版本和编译版本的区别与应用实践-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利
1: 测试数据准备
在ArkTS中,由于语言的特性,无法直接对明确的类型数据进行直接强转,故先将该不同类型的数据存到Record中,之后再使用。
// 声明一个变量testRecord,其中包含,字符串、类、数组、Record等不同类型的数据,可以通过testRecord获取对应的不同类型数据
// 定义接口 interface Animal { name: string; eat(food: string): void; }
// 类实现接口
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(food: string) {
console.log(${this.name} is eating ${food});
}
}
// 类实例 let myDog: Animal = new Dog("八万"); // 起个名叫八万 myDog.eat("肉包子"); // 八万吃个肉包子 let tRecord:Record<string,number> = {"number_key":666}; let tArray:Array<string> = ["str1","str2"]; // Record数据,key是string类型,value是Object类型,当Value支持多种类型时,常见写法 let testRecord:Record<string,Object> = {"str_key":"strValue","array_key":tArray,"class_key":myDog,"record_key":tRecord}; // 或写成多种类型的组合 // let testRecord:Record<string,string | Array<string> | Dog | Record<string,number> > = {"str_key":"strValue", "array_key":tArray,"class_key":myDog,"record_key":tRecord};
2:复杂数据类型的有效性验证问题
在ArkTS语言中,基础数据类型可通过 typeof 来判断(比如 string、number、undefined、null),但是对于复杂的数据类型,typeof该变量的取值为"object",下面的代码分别将Array、类、Record的变量的typeof取值以Log的方式输出。
// Array、类、Record等非基础类型,使用typeof时,结果为 “object”,无法区分 console.log("俩毛豆: typeof testRecord["array_key"] " + typeof testRecord["array_key"]); console.log("俩毛豆: typeof testRecord["class_key"] " + typeof testRecord["class_key"]); console.log("俩毛豆: typeof testRecord["record_key"] " + typeof testRecord["record_key"]);
输出的日志截图如下,typeof取值均为 object,如果仅是对其值是否有值的判断(不为null或不为undefined),会存在数据有效性判断不符合预期的情况。

3: Array类型数据的转换及识别
在ArkTS语言中,可以使用instanceof和Array.isArray来判断某个变量是否为Array类型,如下示例。
if (testRecord["array_key"] instanceof Array) { console.log("俩毛豆: testRecord[array_key] is instanceof Array true"); } else { console.log("俩毛豆: testRecord[array_key] is instanceof Array false"); }
if (Array.isArray(testRecord["array_key"])) { console.log("俩毛豆: Array.isArray(testRecord[array_key]) ture"); } else { console.log("俩毛豆: Array.isArray(testRecord[array_key]) false"); }
if (testRecord["class_key"] instanceof Array) { console.log("俩毛豆: testRecord[class_key] is instanceof Array true"); } else { console.log("俩毛豆: testRecord[class_key] is instanceof Array false"); }
if (testRecord["record_key"] instanceof Array) { console.log("俩毛豆: testRecord[record_key] is instanceof Array true"); } else { console.log("俩毛豆: testRecord[record_key] is instanceof Array false"); }
对应的log输出及编译调试时的变量状态的截图,可以看出,类对象实例和Record变量被判定为非Array。在实际研发过程中,可先对其进行类型的判断,再进行下一步的数据处理即可。

4: Record类型数据的转换及识别
在ArkTS的原生API中没有提供直接判断是否为Record的方法,可以封装一个函数进行基本的判断。下面的示例实现了判定是否为Record的方法,但这种方式,如果是类实例,也会返回 true,如果需要强效验,可以在此基础检查所有key的类型及值,按需定制,在这里不作过多介绍。
function isRecord(obj: Object): boolean { // 检查是否为object 且 非null/undefine的数组 // 这种方式,如果是类实例,也会返回 true if (typeof obj !== 'object' || obj === null || obj === undefined || Array.isArray(obj)) { return false; } return true; }
对应的验证示例
if (isRecord(testRecord["array_key"])) { console.log("俩毛豆: testRecord[array_key] is Record true"); } else { console.log("俩毛豆: testRecord[array_key] is Record false"); } if (isRecord(testRecord["class_key"])) { console.log("俩毛豆: testRecord[class_key] is Record true"); } else { console.log("俩毛豆: testRecord[class_key] is Record false"); } if (isRecord(testRecord["record_key"])) { console.log("俩毛豆: testRecord[record_key] is Record true"); } else { console.log("俩毛豆: testRecord[record_key] is Record false"); }
对应的log输出的截图,可以看出,类对象实例和Record,结果判断均为true,这时需要对实际的Record中记录的key及Value进行判断

4.1 类对象实例与Record的互转
如果对类对象实例进行Record的取值操作,得到的结果是什么呢?下面分别将类对象实例及Record变量进行取值操作。
let classObjAsRecord:Record<"string",Object> = testRecord["class_key"] as Record<"string",Object>; let recordObjAsRecord:Record<"string",Object> = testRecord["record_key"] as Record<"string",Object>; console.log("俩毛豆: classObjAsRecord[number_key] = " + classObjAsRecord["number_key"]); console.log("俩毛豆: recordObjAsRecord[number_key] = " + recordObjAsRecord["number_key"]);
对应的执行结果Log如下截图,可以看出,当对类对像进行Record的取值操作时,该值为undefined,并没有产生异常。也就是说对于Record变量进行取值,进行二次效验是有必要且可行。

5: 类实例的转换及识别
在ArkTS语言中,可以使用instanceof来判断某个变量是否为某个类,如下示例,分别判断不同类的类型是否为Dog类。
// instanceof array_key if (testRecord["array_key"] instanceof Dog) { console.log("俩毛豆: testRecord[array_key] is instanceof Dog true"); } else { console.log("俩毛豆: testRecord[array_key] is instanceof Dog false"); } // instanceof class_key if (testRecord["class_key"] instanceof Dog) { console.log("俩毛豆: testRecord[class_key] is instanceof Dog true"); } else { console.log("俩毛豆: testRecord[class_key] is instanceof Dog false"); } // instanceof record_key if (testRecord["record_key"] instanceof Dog) { console.log("俩毛豆: testRecord[record_key] is instanceof Dog true"); } else { console.log("俩毛豆: testRecord[record_key] is instanceof Dog false"); }
对应的log输出的截图,可以看出,这种方式的判定是有效的。

5.1 接口类的识别
在实际的研发工作中,一些场景需要判定某个类对象实例是否属于实现某个接口类,之后再进行接口的调用。如果类对象实例没有实现该接口,直接进行调用,则会出现异常。
let recordObjAsClass:Animal = testRecord["record_key"] as Animal; recordObjAsClass.eat("骨头")
如下图所示,错误的原因为类型错误,不可调用。

接口类的判定,在ArkTS语言中没有基础的API为研发人员使用,可以按下思路实现判定的函数。 假设我们不确定某个变量是否实现了 Animal(第1小节中有介绍) 接口, 可以检查是类实例对象否存在接口中定义的必需属性和方法,并验证类型即可。下面为代码示例
function isAnimalInterface(obj: Animal) { return obj && typeof obj.name === 'string' && typeof obj.eat === 'function'; } if (isAnimalInterface(recordObjAsClass)) { recordObjAsClass.eat("骨头"); } else { console.log("俩毛豆:recordObjAsClass 没有实现 Animal接口"); }
对应的执行Log输出截图如下,可以看出,对是否实现于接口类的判定,可以使用上述的方法。实际运行时,obj.name和obj.eat是undefined。比较有经验的同学们,会用可选链操作符如 recordObjAsClass.eat?.("骨头"),这种写法也可以规避异常的产生。

总结:
在本篇的内容中基于HarmonyOS开发中复杂数据类型的转换问题展开,目标是有效的、稳定的使用数据,并针对ArkTS语言中Array、Record和类实例的识别与转换提供了实用解决方案。这些技术点不仅是开发中的常见痛点,也可作为面试考察内容,为HarmonyOS开发者提供了解决数据类型转换问题的系统性思路。
最后再打下广告,对本书感兴趣的同学可以点击以下链接进行购买,或者了解我的班级参加 班级共同学习,点击链接可进入(华为官方活动)
《【鸿蒙生态共建】一文说清复杂类型数据的非预期输入转换与兜底-《精通HarmonyOS NEXT :鸿蒙App开发入门与项目化实战》读者福利》 是转载文章,点击查看原文。