1# 适配指导案例 2 3本文通过更多应用场景中的案例,提供在ArkTS语法规则下将TS代码适配成ArkTS代码的建议。各章以ArkTS语法规则英文名称命名,每个案例提供适配前的TS代码和适配后的ArkTS代码。 4 5## arkts-identifiers-as-prop-names 6 7**应用代码** 8 9```typescript 10interface W { 11 bundleName: string 12 action: string 13 entities: string[] 14} 15 16let wantInfo: W = { 17 'bundleName': 'com.huawei.hmos.browser', 18 'action': 'ohos.want.action.viewData', 19 'entities': ['entity.system.browsable'] 20} 21``` 22 23**建议改法** 24 25```typescript 26interface W { 27 bundleName: string 28 action: string 29 entities: string[] 30} 31 32let wantInfo: W = { 33 bundleName: 'com.huawei.hmos.browser', 34 action: 'ohos.want.action.viewData', 35 entities: ['entity.system.browsable'] 36} 37``` 38 39## arkts-no-any-unknown 40 41### 按照业务逻辑,将代码中的`any, unknown`改为具体的类型 42 43```typescript 44function printObj(obj: any) { 45 console.log(obj); 46} 47 48printObj('abc'); 49``` 50 51**建议改法** 52 53```typescript 54function printObj(obj: string) { 55 console.log(obj); 56} 57 58printObj('abc'); 59``` 60 61### 标注JSON.parse返回值类型 62 63**应用代码** 64 65```typescript 66class A { 67 v: number = 0 68 s: string = '' 69 70 foo(str: string) { 71 let tmpStr = JSON.parse(str); 72 if (tmpStr.add != undefined) { 73 this.v = tmpStr.v; 74 this.s = tmpStr.s; 75 } 76 } 77} 78``` 79 80**建议改法** 81 82```typescript 83class A { 84 v: number = 0 85 s: string = '' 86 87 foo(str: string) { 88 let tmpStr: Record<string, Object> = JSON.parse(str); 89 if (tmpStr.add != undefined) { 90 this.v = tmpStr.v as number; 91 this.s = tmpStr.s as string; 92 } 93 } 94} 95``` 96 97### 使用Record类型 98 99**应用代码** 100 101```typescript 102function printProperties(obj: any) { 103 console.log(obj.name); 104 console.log(obj.value); 105} 106``` 107 108**建议改法** 109 110```typescript 111function printProperties(obj: Record<string, Object>) { 112 console.log(obj.name as string); 113 console.log(obj.value as string); 114} 115``` 116 117## arkts-no-call-signature 118 119使用函数类型来替代。 120 121**应用代码** 122 123```typescript 124interface I { 125 (value: string): void; 126} 127 128function foo(fn: I) { 129 fn('abc'); 130} 131 132foo((value: string) => { 133 console.log(value); 134}) 135``` 136 137 138**建议改法** 139 140```typescript 141type I = (value: string) => void 142 143function foo(fn: I) { 144 fn('abc'); 145} 146 147foo((value: string) => { 148 console.log(value); 149}) 150``` 151 152## arkts-no-ctor-signatures-type 153 154**应用代码** 155 156```typescript 157class Controller { 158 value: string = '' 159 160 constructor(value: string) { 161 this.value = value; 162 } 163} 164 165type ControllerConstructor = { 166 new (value: string): Controller; 167} 168 169class Menu { 170 controller: ControllerConstructor = Controller 171 createController() { 172 if (this.controller) { 173 return new this.controller(123); 174 } 175 return null; 176 } 177} 178 179let t = new Menu(); 180console.log(t.createController()!.value); 181``` 182 183**建议改法** 184 185```typescript 186class Controller { 187 value: string = '' 188 189 constructor(value: string) { 190 this.value = value; 191 } 192} 193 194type ControllerConstructor = () => Controller; 195 196class Menu { 197 controller: ControllerConstructor = () => { 198 return new Controller('abc'); 199 } 200 201 createController() { 202 if (this.controller) { 203 return this.controller(); 204 } 205 return null; 206 } 207} 208 209let t: Menu = new Menu(); 210console.log(t.createController()!.value); 211``` 212 213## arkts-no-indexed-signatures 214 215使用Record类型来替代。 216 217**应用代码** 218 219```typescript 220function foo(data: { [key: string]: string }) { 221 data['a'] = 'a'; 222 data['b'] = 'b'; 223 data['c'] = 'c'; 224} 225``` 226 227**建议改法** 228 229```typescript 230function foo(data: Record<string, string>) { 231 data['a'] = 'a'; 232 data['b'] = 'b'; 233 data['c'] = 'c'; 234} 235``` 236 237## arkts-no-typing-with-this 238 239**应用代码** 240 241```typescript 242class C { 243 getInstance(): this { 244 return this; 245 } 246} 247``` 248 249**建议改法** 250 251```typescript 252class C { 253 getInstance(): C { 254 return this; 255 } 256} 257``` 258 259## arkts-no-ctor-prop-decls 260 261**应用代码** 262 263```typescript 264class Person { 265 constructor(readonly name: string) {} 266 267 getName(): string { 268 return this.name; 269 } 270} 271``` 272 273**建议改法** 274 275```typescript 276class Person { 277 name: string 278 constructor(name: string) { 279 this.name = name; 280 } 281 282 getName(): string { 283 return this.name; 284 } 285} 286``` 287 288## arkts-no-ctor-signatures-iface 289 290**应用代码** 291 292```typescript 293class Controller { 294 value: string = '' 295 296 constructor(value: string) { 297 this.value = value; 298 } 299} 300 301interface ControllerConstructor { 302 new (value: string): Controller; 303} 304 305class Menu { 306 controller: ControllerConstructor = Controller 307 createController() { 308 if (this.controller) { 309 return new this.controller('abc'); 310 } 311 return null; 312 } 313} 314 315let t = new Menu(); 316console.log(t.createController()!.value); 317``` 318 319**建议改法** 320 321```typescript 322class Controller { 323 value: string = '' 324 325 constructor(value: string) { 326 this.value = value; 327 } 328} 329 330type ControllerConstructor = () => Controller; 331 332class Menu { 333 controller: ControllerConstructor = () => { 334 return new Controller('abc'); 335 } 336 337 createController() { 338 if (this.controller) { 339 return this.controller(); 340 } 341 return null; 342 } 343} 344 345let t: Menu = new Menu(); 346console.log(t.createController()!.value); 347``` 348 349## arkts-no-props-by-index 350 351可以转换成Record类型,用来访问对象的属性。 352 353**应用代码** 354 355```typescript 356import myRouter from '@ohos.router'; 357let params: Object = myRouter.getParams(); 358let funNum: number = params['funNum']; 359let target: string = params['target']; 360``` 361 362**建议改法** 363 364```typescript 365import myRouter from '@ohos.router'; 366let params = myRouter.getParams() as Record<string, string | number>; 367let funNum: number = params.funNum as number; 368let target: string = params.target as string; 369``` 370 371## arkts-no-inferred-generic-params 372 373**应用代码** 374 375```typescript 376class A { 377 str: string = '' 378} 379class B extends A {} 380class C extends A {} 381 382let arr: Array<A> = []; 383 384let originMenusMap:Map<string, C> = new Map(arr.map(item => [item.str, (item instanceof C) ? item: null])); 385``` 386 387**建议改法** 388 389```typescript 390class A { 391 str: string = '' 392} 393class B extends A {} 394class C extends A {} 395 396let arr: Array<A> = []; 397 398let originMenusMap: Map<string, C | null> = new Map<string, C | null>(arr.map<[string, C | null]>(item => [item.str, (item instanceof C) ? item: null])); 399``` 400 401**原因** 402 403`(item instanceof C) ? item: null` 需要声明类型为`C | null`,由于编译器无法推导出`map`的泛型类型参数,需要显式标注。 404 405## arkts-no-regexp-literals 406 407**应用代码** 408 409```typescript 410let regex: RegExp = /\s*/g; 411``` 412 413**建议改法** 414 415```typescript 416let regexp: RegExp = new RegExp('\\s*','g'); 417``` 418 419**原因** 420 421如果正则表达式中使用了标志符,需要将其作为`new RegExp()`的参数。 422 423## arkts-no-untyped-obj-literals 424 425### 从SDK中导入类型,标注object literal类型 426 427**应用代码** 428 429```typescript 430const area = { 431 pixels: new ArrayBuffer(8), 432 offset: 0, 433 stride: 8, 434 region: { size: { height: 1,width:2 }, x: 0, y: 0 } 435} 436``` 437 438**建议改法** 439 440```typescript 441import image from '@ohos.multimedia.image'; 442 443const area: image.PositionArea = { 444 pixels: new ArrayBuffer(8), 445 offset: 0, 446 stride: 8, 447 region: { size: { height: 1, width: 2 }, x: 0, y: 0 } 448} 449``` 450 451### 用class为object literal标注类型,需要class的构造函数无参数 452 453**应用代码** 454 455```typescript 456class Test { 457 value: number = 1 458 459 constructor(value: number) { 460 this.value = value; 461 } 462} 463 464let t: Test = { value: 2 }; 465``` 466 467**建议改法1** 468 469```typescript 470// 去除构造函数 471class Test { 472 value: number = 1 473} 474 475let t: Test = { value: 2 }; 476``` 477 478**建议改法2** 479```typescript 480// 使用new 481class Test { 482 value: number = 1 483 484 constructor(value: number) { 485 this.value = value; 486 } 487} 488 489let t: Test = new Test(2); 490``` 491 492**原因** 493 494```typescript 495class C { 496 value: number = 1 497 498 constructor(n: number) { 499 if (n < 0) { 500 throw new Error('Negative'); 501 } 502 this.value = n; 503 } 504} 505 506let s: C = new C(-2); //抛出异常 507let t: C = { value: -2 }; //ArkTS不支持 508``` 509 510例如在上面的例子中,如果允许使用`C`来标注object literal的类型,那么上述代码中的变量`t`会导致行为的二义性。ArkTS禁止通过object literal来绕过这一行为。 511 512### 用class/interface为object literal标注类型,需要使用identifier作为object literal的key 513 514**应用代码** 515 516```typescript 517class Test { 518 value: number = 0 519} 520 521let arr: Test[] = [ 522 { 523 'value': 1 524 }, 525 { 526 'value': 2 527 }, 528 { 529 'value': 3 530 } 531] 532``` 533 534**建议改法** 535 536```typescript 537class Test { 538 value: number = 0 539} 540let arr: Test[] = [ 541 { 542 value: 1 543 }, 544 { 545 value: 2 546 }, 547 { 548 value: 3 549 } 550] 551``` 552 553### 使用Record为object literal标注类型,需要使用字符串作为object literal的key 554 555**应用代码** 556 557```typescript 558let obj: Record<string, number | string> = { 559 value: 123, 560 name: 'abc' 561} 562``` 563 564**建议改法** 565 566```typescript 567let obj: Record<string, number | string> = { 568 'value': 123, 569 'name': 'abc' 570} 571``` 572 573### 函数参数类型包含index signature 574 575**应用代码** 576 577```typescript 578function foo(obj: { [key: string]: string}): string { 579 if (obj != undefined && obj != null) { 580 return obj.value1 + obj.value2; 581 } 582 return ''; 583} 584``` 585 586**建议改法** 587 588```typescript 589function foo(obj: Record<string, string>): string { 590 if (obj != undefined && obj != null) { 591 return obj.value1 + obj.value2; 592 } 593 return ''; 594} 595``` 596 597### 函数实参使用了object literal 598 599**应用代码** 600 601```typescript 602(fn) => { 603 fn({ value: 123, name:'' }); 604} 605``` 606 607**建议改法** 608 609```typescript 610class T { 611 value: number = 0 612 name: string = '' 613} 614 615(fn: (v: T) => void) => { 616 fn({ value: 123, name: '' }); 617} 618``` 619 620### class/interface 中包含方法 621 622**应用代码** 623 624```typescript 625interface T { 626 foo(value: number): number 627} 628 629let t:T = { foo: (value) => { return value } }; 630``` 631 632**建议改法1** 633 634```typescript 635interface T { 636 foo: (value: number) => number 637} 638 639let t:T = { foo: (value) => { return value } }; 640``` 641 642**建议改法2** 643 644```typescript 645class T { 646 foo: (value: number) => number = (value: number) => { 647 return value; 648 } 649} 650 651let t:T = new T(); 652``` 653 654**原因** 655 656class/interface中声明的方法应该被所有class的实例共享。ArkTS不支持通过object literal改写实例方法。ArkTS支持函数类型的属性。 657 658### export default对象 659 660**应用代码** 661 662```typescript 663import hilog from '@ohos.hilog' 664 665export default { 666 onCreate() { 667 hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate'); 668 }, 669 onDestroy() { 670 hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy'); 671 } 672} 673``` 674 675**建议改法** 676 677```typescript 678import hilog from '@ohos.hilog' 679 680class Test { 681 onCreate() { 682 hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate'); 683 } 684 onDestroy() { 685 hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy'); 686 } 687} 688 689export default new Test() 690``` 691 692### 通过导入namespace获取类型 693 694**应用代码** 695 696```typescript 697import { BusinessError } from '@ohos.base'; 698import bluetooth from '@ohos.bluetooth'; 699let serverNumber = -1; 700function serverSocket(code: BusinessError, num: number) { 701 console.log('bluetooth error code: ' + code.code); 702 if (code.code == 0) { 703 console.log('bluetooth serverSocket Number: ' + num); 704 serverNumber = num; 705 } 706} 707 708let sppOption = { uuid: '', secure: false, type: 0 }; 709bluetooth.sppListen('', sppOption, serverSocket); 710``` 711 712**建议改法** 713 714```typescript 715import { BusinessError } from '@ohos.base'; 716import bluetooth from '@ohos.bluetooth'; 717let serverNumber = -1; 718function serverSocket(code: BusinessError, num: number) { 719 console.log('bluetooth error code: ' + code.code); 720 if (code.code == 0) { 721 console.log('bluetooth serverSocket Number: ' + num); 722 serverNumber = num; 723 } 724} 725 726let sppOption: bluetooth.SppOption = { uuid: '', secure: false, type: 0 }; 727bluetooth.sppListen('', sppOption, serverSocket); 728``` 729 730**原因** 731 732对象字面量缺少类型,根据`bluetooth.sppListen`分析可以得知,`sppOption`的类型来源于SDK,那么只需要将类型导入即可。 733注意到在`@ohos.bluetooth`中,`sppOption`是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。 734 735### object literal传参给Object类型 736 737**应用代码** 738 739```typescript 740function emit(event: string, ...args: Object[]): void {} 741 742emit('', { 743 'action': 11, 744 'outers': false 745}); 746``` 747 748**建议改法** 749 750```typescript 751function emit(event: string, ...args: Object[]): void {} 752 753let emitArg: Record<string, number | boolean> = { 754 'action': 11, 755 'outers': false 756} 757 758emit('', emitArg); 759``` 760 761## arkts-no-obj-literals-as-types 762 763**应用代码** 764 765```typescript 766type Person = { name: string, age: number } 767``` 768 769**建议改法** 770 771```typescript 772interface Person { 773 name: string, 774 age: number 775} 776``` 777 778## arkts-no-noninferrable-arr-literals 779 780**应用代码** 781 782```typescript 783let permissionList = [ 784 { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' }, 785 { name: '麦克风', value: '用于反馈问题单时增加语音' }, 786 { name: '存储', value: '用于反馈问题单时增加本地文件附件' } 787] 788``` 789 790**建议改法** 791 792为对象字面量声明类型 793 794```typescript 795class PermissionItem { 796 name?: string 797 value?: string 798} 799 800let permissionList: PermissionItem[] = [ 801 { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' }, 802 { name: '麦克风', value: '用于反馈问题单时增加语音' }, 803 { name: '存储', value: '用于反馈问题单时增加本地文件附件' } 804] 805``` 806 807## arkts-no-method-reassignment 808 809**应用代码** 810 811```typescript 812class C { 813 add(left: number, right: number): number { 814 return left + right; 815 } 816} 817 818function sub(left: number, right: number): number { 819 return left - right; 820} 821 822let c1 = new C(); 823c1.add = sub; 824``` 825 826**建议改法** 827 828```typescript 829class C { 830 add: (left: number, right: number) => number = 831 (left: number, right: number) => { 832 return left + right; 833 } 834} 835 836function sub(left: number, right: number): number { 837 return left - right; 838} 839 840let c1 = new C(); 841c1.add = sub; 842``` 843 844## arkts-no-polymorphic-unops 845 846**应用代码** 847 848```typescript 849let a = +'5'; 850let b = -'5'; 851let c = ~'5'; 852let d = +'string'; 853``` 854 855**建议改法** 856 857```typescript 858let a = Number.parseInt('5'); 859let b = -Number.parseInt('5'); 860let c = ~Number.parseInt('5'); 861let d = new Number('string'); 862``` 863 864## arkts-no-type-query 865 866**应用代码** 867 868```typescript 869// module1.ts 870class C { 871 value: number = 0 872} 873 874export let c = new C() 875 876// module2.ts 877import { c } from './module1' 878let t: typeof c = { value: 123 }; 879``` 880 881**建议改法** 882 883```typescript 884// module1.ts 885class C { 886 value: number = 0 887} 888 889export { C } 890 891// module2.ts 892import { C } from './module1' 893let t: C = { value: 123 }; 894``` 895 896## arkts-no-in 897 898### 使用Object.keys判断属性是否存在 899 900**应用代码** 901 902```typescript 903function test(str: string, obj: Record<string, Object>) { 904 return str in obj; 905} 906``` 907 908**建议改法** 909 910```typescript 911function test(str: string, obj: Record<string, Object>) { 912 for (let i of Object.keys(obj)) { 913 if (i == str) { 914 return true; 915 } 916 } 917 return false; 918} 919``` 920 921## arkts-no-destruct-assignment 922 923**应用代码** 924 925```typescript 926let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 927for (let [key, value] of map) { 928 console.log(key); 929 console.log(value); 930} 931``` 932 933**建议改法** 934 935使用数组 936 937```typescript 938let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 939for (let arr of map) { 940 let key = arr[0]; 941 let value = arr[1]; 942 console.log(key); 943 console.log(value); 944} 945``` 946 947## arkts-no-types-in-catch 948 949**应用代码** 950 951```typescript 952import { BusinessError } from '@ohos.base'; 953 954try { 955 // ... 956} catch (e: BusinessError) { 957 logger.error(e.code, e.message); 958} 959``` 960 961**建议改法** 962 963```typescript 964import { BusinessError } from '@ohos.base'; 965 966try { 967 // ... 968} catch (error) { 969 let e: BusinessError = error as BusinessError; 970 logger.error(e.code, e.message); 971} 972``` 973 974## arkts-no-for-in 975 976**应用代码** 977 978```typescript 979interface Person { 980 [name: string]: string 981} 982let p: Person = { 983 name: 'tom', 984 age: '18' 985}; 986 987for (let t in p) { 988 console.log(p[t]); 989} 990``` 991 992**建议改法** 993 994```typescript 995let p: Record<string, string> = { 996 'name': 'tom', 997 'age': '18' 998}; 999 1000for (let ele of Object.entries(p)) { 1001 console.log(ele[1]); 1002} 1003``` 1004 1005## arkts-no-mapped-types 1006 1007**应用代码** 1008 1009```typescript 1010class C { 1011 a: number = 0 1012 b: number = 0 1013 c: number = 0 1014} 1015type OptionsFlags = { 1016 [Property in keyof C]: string 1017} 1018``` 1019 1020**建议改法** 1021 1022```typescript 1023class C { 1024 a: number = 0 1025 b: number = 0 1026 c: number = 0 1027} 1028 1029type OptionsFlags = Record<keyof C, string> 1030``` 1031 1032## arkts-limited-throw 1033 1034**应用代码** 1035 1036```typescript 1037import { BusinessError } from '@ohos.base'; 1038 1039function ThrowError(error: BusinessError) { 1040 throw error; 1041} 1042``` 1043 1044**建议改法** 1045 1046```typescript 1047import { BusinessError } from '@ohos.base'; 1048 1049function ThrowError(error: BusinessError) { 1050 throw error as Error; 1051} 1052``` 1053 1054**原因** 1055 1056`throw`语句中值的类型必须为`Error`或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用`as`将类型转换为`Error`。 1057 1058## arkts-no-standalone-this 1059 1060### 函数内使用this 1061 1062**应用代码** 1063 1064```typescript 1065function foo() { 1066 console.log(this.value); 1067} 1068 1069let obj = { value: 'abc' }; 1070foo.apply(obj); 1071``` 1072 1073**建议改法1** 1074 1075使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制 1076 1077```typescript 1078class Test { 1079 value: string = '' 1080 constructor (value: string) { 1081 this.value = value 1082 } 1083 1084 foo() { 1085 console.log(this.value); 1086 } 1087} 1088 1089let obj: Test = new Test('abc'); 1090obj.foo(); 1091``` 1092 1093**建议改法2** 1094 1095将this作为参数传入 1096 1097```typescript 1098function foo(obj: Test) { 1099 console.log(obj.value); 1100} 1101 1102class Test { 1103 value: string = '' 1104} 1105 1106let obj: Test = { value: 'abc' }; 1107foo(obj); 1108``` 1109 1110**建议改法3** 1111 1112将属性作为参数传入 1113```typescript 1114function foo(value: string) { 1115 console.log(value); 1116} 1117 1118class Test { 1119 value: string = '' 1120} 1121 1122let obj: Test = { value: 'abc' }; 1123foo(obj.value); 1124``` 1125 1126### class的静态方法内使用this 1127 1128**应用代码** 1129 1130```typescript 1131class Test { 1132 static value: number = 123 1133 static foo(): number { 1134 return this.value 1135 } 1136} 1137``` 1138 1139**建议改法** 1140 1141```typescript 1142class Test { 1143 static value: number = 123 1144 static foo(): number { 1145 return Test.value 1146 } 1147} 1148``` 1149 1150## arkts-no-spread 1151 1152**应用代码** 1153 1154```typescript 1155import notification from '@ohos.notificationManager'; 1156 1157function buildNotifyLongRequest(): notification.NotificationRequest { 1158 // ... 1159} 1160 1161let notificationRequest: notification.NotificationRequest = { 1162 ...buildNotifyLongRequest(), 1163 deliveryTime: new Date().getTime() 1164} 1165``` 1166 1167**建议改法** 1168 1169```typescript 1170import notification from '@ohos.notificationManager'; 1171 1172function buildNotifyLongRequest():notification.NotificationRequest { 1173 // ... 1174} 1175 1176let notificationRequest: notification.NotificationRequest = buildNotifyLongRequest(); 1177notificationRequest.deliveryTime = new Date().getTime(); 1178``` 1179 1180**原因** 1181 1182ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。 1183 1184## arkts-no-ctor-signatures-funcs 1185 1186在class内声明属性,而不是在构造函数上。 1187 1188**应用代码** 1189 1190```typescript 1191class Controller { 1192 value: string = '' 1193 constructor(value: string) { 1194 this.value = value 1195 } 1196} 1197 1198type ControllerConstructor = new (value: string) => Controller; 1199 1200class Menu { 1201 controller: ControllerConstructor = Controller 1202 createController() { 1203 if (this.controller) { 1204 return new this.controller('abc'); 1205 } 1206 return null; 1207 } 1208} 1209 1210let t = new Menu() 1211console.log(t.createController()!.value) 1212``` 1213 1214**建议改法** 1215 1216```typescript 1217class Controller { 1218 value: string = '' 1219 constructor(value: string) { 1220 this.value = value; 1221 } 1222} 1223 1224type ControllerConstructor = () => Controller; 1225 1226class Menu { 1227 controller: ControllerConstructor = () => { return new Controller('abc') } 1228 createController() { 1229 if (this.controller) { 1230 return this.controller(); 1231 } 1232 return null; 1233 } 1234} 1235 1236let t: Menu = new Menu(); 1237console.log(t.createController()!.value); 1238``` 1239 1240## arkts-no-globalthis 1241 1242由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。 1243 12441. 建议按照业务逻辑根据`import/export`语法实现数据在不同模块的传递。 1245 12462. 必要情况下,可以通过构造的**单例对象**来实现全局对象的功能。(**说明:**不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。) 1247 1248**构造单例对象** 1249 1250```typescript 1251// 构造单例对象 1252export class GlobalContext { 1253 private constructor() {} 1254 private static instance: GlobalContext; 1255 private _objects = new Map<string, Object>(); 1256 1257 public static getContext(): GlobalContext { 1258 if (!GlobalContext.instance) { 1259 GlobalContext.instance = new GlobalContext(); 1260 } 1261 return GlobalContext.instance; 1262 } 1263 1264 getObject(value: string): Object | undefined { 1265 return this._objects.get(value); 1266 } 1267 1268 setObject(key: string, objectClass: Object): void { 1269 this._objects.set(key, objectClass); 1270 } 1271} 1272 1273``` 1274 1275**应用代码** 1276 1277```typescript 1278 1279// file1.ts 1280 1281export class Test { 1282 value: string = ''; 1283 foo(): void { 1284 globalThis.value = this.value; 1285 } 1286} 1287 1288// file2.ts 1289 1290globalThis.value; 1291 1292``` 1293 1294**建议改法** 1295 1296```typescript 1297 1298// file1.ts 1299 1300import { GlobalContext } from '../GlobalContext' 1301 1302export class Test { 1303 value: string = ''; 1304 foo(): void { 1305 GlobalContext.getContext().setObject('value', this.value); 1306 } 1307} 1308 1309// file2.ts 1310 1311import { GlobalContext } from '../GlobalContext' 1312 1313GlobalContext.getContext().getObject('value'); 1314``` 1315 1316## arkts-no-func-apply-bind-call 1317 1318### 使用标准库中接口 1319 1320**应用代码** 1321 1322```typescript 1323let arr: number[] = [1, 2, 3, 4]; 1324let str = String.fromCharCode.apply(null, Array.from(arr)); 1325``` 1326 1327**建议改法** 1328 1329```typescript 1330let arr: number[] = [1, 2, 3, 4]; 1331let str = String.fromCharCode(...Array.from(arr)); 1332``` 1333 1334### bind定义方法 1335 1336**应用代码** 1337 1338```typescript 1339class A { 1340 value: string = '' 1341 foo: Function = () => {} 1342} 1343 1344class Test { 1345 value: string = '1234' 1346 obj: A = { 1347 value: this.value, 1348 foo: this.foo.bind(this) 1349 } 1350 1351 foo() { 1352 console.log(this.value); 1353 } 1354} 1355``` 1356 1357**建议改法1** 1358 1359```typescript 1360class A { 1361 value: string = '' 1362 foo: Function = () => {} 1363} 1364 1365class Test { 1366 value: string = '1234' 1367 obj: A = { 1368 value: this.value, 1369 foo: (): void => this.foo() 1370 } 1371 1372 foo() { 1373 console.log(this.value); 1374 } 1375} 1376``` 1377 1378**建议改法2** 1379 1380```typescript 1381class A { 1382 value: string = '' 1383 foo: Function = () => {} 1384} 1385 1386class Test { 1387 value: string = '1234' 1388 foo: () => void = () => { 1389 console.log(this.value); 1390 } 1391 obj: A = { 1392 value: this.value, 1393 foo: this.foo 1394 } 1395} 1396``` 1397 1398### 使用apply 1399 1400**应用代码** 1401 1402```typescript 1403class A { 1404 value: string; 1405 constructor (value: string) { 1406 this.value = value; 1407 } 1408 1409 foo() { 1410 console.log(this.value); 1411 } 1412} 1413 1414let a1 = new A('1'); 1415let a2 = new A('2'); 1416 1417a1.foo(); 1418a1.foo.apply(a2); 1419``` 1420 1421**建议改法** 1422 1423```typescript 1424class A { 1425 value: string; 1426 constructor (value: string) { 1427 this.value = value; 1428 } 1429 1430 foo() { 1431 this.fooApply(this); 1432 } 1433 1434 fooApply(a: A) { 1435 console.log(a.value); 1436 } 1437} 1438 1439let a1 = new A('1'); 1440let a2 = new A('2'); 1441 1442a1.foo(); 1443a1.fooApply(a2); 1444``` 1445 1446## arkts-limited-stdlib 1447 1448### `Object.fromEntries()` 1449 1450**应用代码** 1451 1452```typescript 1453let entries = new Map([ 1454 ['foo', 123], 1455 ['bar', 456] 1456]); 1457 1458let obj = Object.fromEntries(entries); 1459``` 1460 1461**建议改法** 1462 1463```typescript 1464let entries = new Map([ 1465 ['foo', 123], 1466 ['bar', 456] 1467]); 1468 1469let obj: Record<string, Object> = {}; 1470entries.forEach((value, key) => { 1471 if (key != undefined && key != null) { 1472 obj[key] = value; 1473 } 1474}) 1475``` 1476 1477### 使用`Number`的属性和方法 1478 1479ArkTS不允许使用全局对象的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt` 1480 1481可以使用`Number`的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt` 1482 1483**应用代码** 1484 1485```typescript 1486NaN; 1487isFinite(123); 1488parseInt('123'); 1489``` 1490 1491**建议改法** 1492 1493```typescript 1494Number.NaN; 1495Number.isFinite(123); 1496Number.parseInt('123'); 1497``` 1498 1499## arkts-strict-typing(StrictModeError) 1500 1501### strictPropertyInitialization 1502 1503**应用代码** 1504 1505```typescript 1506interface I { 1507 name:string 1508} 1509 1510class A {} 1511 1512class Test { 1513 a: number; 1514 b: string; 1515 c: boolean; 1516 d: I; 1517 e: A; 1518} 1519 1520``` 1521 1522**建议改法** 1523 1524```typescript 1525interface I { 1526 name:string 1527} 1528 1529class A {} 1530 1531class Test { 1532 a: number; 1533 b: string; 1534 c: boolean; 1535 d: I = { name:'abc' }; 1536 e: A | null = null; 1537 constructor(a:number, b:string, c:boolean) { 1538 this.a = a; 1539 this.b = b; 1540 this.c = c; 1541 } 1542} 1543 1544``` 1545### Type `*** | null` is not assignable to type `***` 1546 1547**应用代码** 1548 1549```typescript 1550class A { 1551 bar() {} 1552} 1553function foo(n: number) { 1554 if (n === 0) { 1555 return null; 1556 } 1557 return new A(); 1558} 1559function getNumber() { 1560 return 5; 1561} 1562let a:A = foo(getNumber()); 1563a.bar(); 1564``` 1565 1566**建议改法** 1567 1568```typescript 1569class A { 1570 bar() {} 1571} 1572function foo(n: number) { 1573 if (n === 0) { 1574 return null; 1575 } 1576 return new A(); 1577} 1578function getNumber() { 1579 return 5; 1580} 1581 1582let a: A | null = foo(getNumber()); 1583a?.bar(); 1584``` 1585 1586### 严格属性初始化检查 1587 1588在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。 1589 1590**建议改法** 1591 15921.一般情况下,**建议按照业务逻辑**在声明时初始化属性,或者在构造函数中为属性赋值。如: 1593 1594```typescript 1595//code with error 1596class Test { 1597 value: number 1598 flag: boolean 1599} 1600 1601//方式一,在声明时初始化 1602class Test { 1603 value: number = 0 1604 flag: boolean = false 1605} 1606 1607//方式二,在构造函数中赋值 1608class Test { 1609 value: number 1610 flag: boolean 1611 constructor(value: number, flag: boolean) { 1612 this.value = value; 1613 this.flag = flag; 1614 } 1615} 1616``` 1617 16182.对于对象类型(包括函数类型)`A`,如果不确定如何初始化,建议按照以下方式之一进行初始化 1619 1620 方式(i) `prop: A | null = null` 1621 1622 方式(ii) `prop?: A` 1623 1624 方式三(iii) `prop: A | undefined = undefined` 1625 1626- 从性能角度来说,`null`类型只用在编译期的类型检查中,对虚拟机的性能无影响。而`undefined | A`被视为联合类型,运行时可能有额外的开销。 1627- 从代码可读性、简洁性的角度来说,`prop?:A`是`prop: A | undefined = undefined`的语法糖,**推荐使用可选属性的写法** 1628 1629### 严格函数类型检查 1630 1631**应用代码** 1632 1633```typescript 1634function foo(fn: (value?: string) => void, value: string): void {} 1635 1636foo((value: string) => {}, ''); //error 1637``` 1638 1639**建议改法** 1640 1641```typescript 1642function foo(fn: (value?: string) => void, value: string): void {} 1643 1644foo((value?: string) => {}, ''); 1645``` 1646 1647**原因** 1648 1649例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在`foo`的函数体中,一个`undefined`被传入`fn`(这是可以的,因为`fn`可以接受`undefined`),但是在代码第6行`foo`的调用点,传入的`(value: string) => { console.log(value.toUpperCase()) }`的函数实现中,始终将参数`value`当做string类型,允许其调用`toUpperCase`方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在`undefined`上无法找到属性的错误。 1650 1651```typescript 1652function foo(fn: (value?: string) => void, value: string): void { 1653 let v: string | undefined = undefined; 1654 fn(v); 1655} 1656 1657foo((value: string) => { console.log(value.toUpperCase()) }, ''); // Cannot read properties of undefined (reading 'toUpperCase') 1658``` 1659 1660为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。 1661 1662### 严格空值检查 1663 1664**应用代码** 1665 1666```typescript 1667class Test { 1668 private value?: string 1669 1670 public printValue () { 1671 console.log(this.value.toLowerCase()); 1672 } 1673} 1674 1675let t = new Test(); 1676t.printValue(); 1677``` 1678 1679**建议改法** 1680 1681在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。 1682 1683```typescript 1684class Test { 1685 private value?: string 1686 1687 public printValue () { 1688 if (this.value) { 1689 console.log(this.value.toLowerCase()); 1690 } 1691 } 1692} 1693 1694let t = new Test(); 1695t.printValue(); 1696``` 1697 1698**原因** 1699 1700在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为`t`的属性`value`为`undefined`(这是因为`value?: string`是`value: string | undefined = undefined`的语法糖),在第11行调用`printValue`方法时,由于在该方法体内未对`this.value`的值进行空值检查,而直接按照`string`类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。 1701 1702### 函数返回类型不匹配 1703 1704**应用代码** 1705 1706```typescript 1707class Test { 1708 handleClick: (action: string, externInfo?: string) => void | null = null; 1709} 1710``` 1711 1712**建议改法** 1713 1714在这种写法下,函数返回类型被解析为 `void | undefined`,需要添加括号用来区分union类型。 1715 1716```typescript 1717class Test { 1718 handleClick: ((action: string, externInfo?: string) => void) | null = null; 1719} 1720``` 1721 1722### '***' is of type 'unknown' 1723 1724**应用代码** 1725 1726```typescript 1727try { 1728 1729} catch (error) { 1730 console.log(error.message); 1731} 1732``` 1733 1734**建议改法** 1735 1736```typescript 1737import { BusinessError } from '@ohos.base' 1738 1739try { 1740 1741} catch (error) { 1742 console.log((error as BusinessError).message); 1743} 1744``` 1745 1746### Type '*** | null' is not assignable to type '\*\*\*' 1747 1748**应用代码** 1749 1750```typescript 1751class A { 1752 value: number 1753 constructor(value: number) { 1754 this.value = value; 1755 } 1756} 1757 1758function foo(v: number): A | null { 1759 if (v > 0) { 1760 return new A(v); 1761 } 1762 return null; 1763} 1764 1765let a: A = foo(); 1766``` 1767 1768**建议改法1** 1769 1770修改变量`a`的类型:`let a: A | null = foo()`。 1771 1772```typescript 1773class A { 1774 value: number 1775 constructor(value: number) { 1776 this.value = value; 1777 } 1778} 1779 1780function foo(v: number): A | null { 1781 if (v > 0) { 1782 return new A(v); 1783 } 1784 return null; 1785} 1786 1787let a: A | null = foo(123); 1788 1789if (a != null) { 1790 // 非空分支 1791} else { 1792 // 处理null 1793} 1794``` 1795 1796**建议改法2** 1797 1798如果可以断定此处调用`foo`一定返回非空值,可以使用非空断言`!`。 1799 1800```typescript 1801class A { 1802 value: number 1803 constructor(value: number) { 1804 this.value = value; 1805 } 1806} 1807 1808function foo(v: number): A | null { 1809 if (v > 0) { 1810 return new A(v); 1811 } 1812 return null; 1813} 1814 1815let a: A = foo(123)!; 1816``` 1817 1818### Cannot invoke an object which possibly 'undefined' 1819 1820**应用代码** 1821 1822```typescript 1823interface A { 1824 foo?: () => void 1825} 1826 1827let a:A = { foo: () => {} }; 1828a.foo(); 1829``` 1830 1831**建议改法1** 1832 1833```typescript 1834interface A { 1835 foo: () => void 1836} 1837let a: A = { foo: () => {} }; 1838a.foo(); 1839``` 1840 1841**建议改法2** 1842 1843```typescript 1844interface A { 1845 foo?: () => void 1846} 1847 1848let a: A = { foo: () => {} }; 1849if (a.foo) { 1850 a.foo(); 1851} 1852``` 1853 1854**原因** 1855 1856在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。 1857 1858### Variable '***' is used before being assigned 1859 1860**应用代码** 1861 1862```typescript 1863class Test { 1864 value: number = 0 1865} 1866 1867let a: Test 1868try { 1869 a = { value: 1}; 1870} catch (e) { 1871 a.value; 1872} 1873a.value; 1874``` 1875 1876**建议改法** 1877 1878```typescript 1879class Test { 1880 value: number = 0 1881} 1882 1883let a: Test | null = null; 1884try { 1885 a = { value:1 }; 1886} catch (e) { 1887 if (a) { 1888 a.value; 1889 } 1890} 1891 1892if (a) { 1893 a.value; 1894} 1895``` 1896 1897**原因** 1898 1899对于primitive types,可以根据业务逻辑赋值,例如0,'',false。 1900 1901对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。 1902 1903### Function lacks ending return statement and return type does not include 'undefined'. 1904 1905**应用代码** 1906 1907```typescript 1908function foo(a: number): number { 1909 if (a > 0) { 1910 return a; 1911 } 1912} 1913``` 1914 1915**建议改法1** 1916 1917根据业务逻辑,在else分支中返回合适的数值 1918 1919**建议改法2** 1920 1921```typescript 1922function foo(a: number): number | undefined { 1923 if (a > 0) { 1924 return a; 1925 } 1926 return 1927} 1928``` 1929 1930## arkts-strict-typing-required 1931 1932**应用代码** 1933 1934```typescript 1935// @ts-nocheck 1936var a: any = 123; 1937``` 1938 1939**建议改法** 1940 1941```typescript 1942let a: number = 123; 1943``` 1944 1945**原因** 1946 1947ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(`// @ts-nocheck`或者`// @ts-ignore`)删去,再根据报错信息修改其他代码。 1948 1949## Importing ArkTS files to JS and TS files is not allowed 1950 1951## arkts-no-tsdeps 1952 1953不允许.ts、.js文件`import`.ets文件源码。 1954 1955**建议改法** 1956 1957方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。 1958 1959方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。 1960 1961## arkts-no-special-imports 1962 1963**应用代码** 1964 1965```typescript 1966import type {A, B, C, D } from '***' 1967``` 1968 1969 1970**建议改法** 1971 1972```typescript 1973import {A, B, C, D } from '***' 1974``` 1975 1976## arkts-no-classes-as-obj 1977 1978### 使用class构造实例 1979 1980**应用代码** 1981 1982```typescript 1983class Controller { 1984 value: string = '' 1985 constructor(value: string) { 1986 this.value = value 1987 } 1988} 1989 1990interface ControllerConstructor { 1991 new (value: string): Controller; 1992} 1993 1994class Menu { 1995 controller: ControllerConstructor = Controller 1996 createController() { 1997 if (this.controller) { 1998 return new this.controller('abc'); 1999 } 2000 return null; 2001 } 2002} 2003 2004let t = new Menu(); 2005console.log(t.createController()!.value); 2006``` 2007 2008**建议改法** 2009 2010```typescript 2011class Controller { 2012 value: string = '' 2013 constructor(value: string) { 2014 this.value = value 2015 } 2016} 2017 2018type ControllerConstructor = () => Controller; 2019 2020class Menu { 2021 controller: ControllerConstructor = () => { return new Controller('abc'); } 2022 createController() { 2023 if (this.controller) { 2024 return this.controller(); 2025 } 2026 return null; 2027 } 2028} 2029 2030let t: Menu = new Menu(); 2031console.log(t.createController()!.value); 2032``` 2033 2034### 访问静态属性 2035 2036**应用代码** 2037 2038```typescript 2039class C1 { 2040 static value: string = 'abc' 2041} 2042 2043class C2 { 2044 static value: string = 'def' 2045} 2046 2047function getValue(obj: any) { 2048 return obj['value']; 2049} 2050 2051console.log(getValue(C1)); 2052console.log(getValue(C2)); 2053``` 2054 2055**建议改法** 2056 2057```typescript 2058class C1 { 2059 static value: string = 'abc' 2060} 2061 2062class C2 { 2063 static value: string = 'def' 2064} 2065 2066function getC1Value(): string { 2067 return C1.value; 2068} 2069 2070function getC2Value(): string { 2071 return C2.value; 2072} 2073 2074console.log(getC1Value()); 2075console.log(getC2Value()); 2076``` 2077 2078## arkts-no-side-effects-imports 2079 2080改用动态import 2081 2082**应用代码** 2083 2084```typescript 2085import 'module' 2086``` 2087 2088**建议改法** 2089 2090```typescript 2091import('module') 2092``` 2093 2094## arkts-no-func-props 2095 2096**应用代码** 2097 2098```typescript 2099function foo(value: number): void { 2100 console.log(value.toString()); 2101} 2102 2103foo.add = (left: number, right: number) => { 2104 return left + right; 2105} 2106 2107foo.sub = (left: number, right: number) => { 2108 return left - right; 2109} 2110``` 2111 2112**建议改法** 2113 2114```typescript 2115class Foo { 2116 static foo(value: number): void { 2117 console.log(value.toString()); 2118 } 2119 2120 static add(left: number, right: number): number { 2121 return left + right; 2122 } 2123 2124 static sub(left: number, right: number): number { 2125 return left - right; 2126 } 2127} 2128``` 2129 2130## 状态管理使用典型场景 2131 2132### Struct组件外使用状态变量 2133 2134由于struct和class不同,不建议把this作为参数传递到struct外部使用,避免引起实例引用无法释放的情况,导致内存泄露。建议将状态变量对象传递到struct外面使用,通过修改对象的属性,来触发UI刷新。 2135 2136**不推荐用法** 2137 2138```typescript 2139export class MyComponentController { 2140 item: MyComponent = null; 2141 2142 setItem(item: MyComponent) { 2143 this.item = item; 2144 } 2145 2146 changeText(value: string) { 2147 this.item.value = value; 2148 } 2149} 2150 2151@Component 2152export default struct MyComponent { 2153 public controller: MyComponentController = null; 2154 @State value: string = 'Hello World'; 2155 2156 build() { 2157 Column() { 2158 Text(this.value) 2159 .fontSize(50) 2160 } 2161 } 2162 2163 aboutToAppear() { 2164 if (this.controller) 2165 this.controller.setItem(this); 2166 } 2167} 2168 2169@Entry 2170@Component 2171struct ObjThisOldPage { 2172 controller = new MyComponentController(); 2173 2174 build() { 2175 Column() { 2176 MyComponent({ controller: this.controller }) 2177 Button('change value').onClick(() => { 2178 this.controller.changeText('Text'); 2179 }) 2180 } 2181 } 2182} 2183``` 2184 2185**推荐用法** 2186 2187```typescript 2188class CC { 2189 value: string = '1'; 2190 2191 constructor(value: string) { 2192 this.value = value; 2193 } 2194} 2195 2196export class MyComponentController { 2197 item: CC = new CC('1'); 2198 2199 setItem(item: CC) { 2200 this.item = item; 2201 } 2202 2203 changeText(value: string) { 2204 this.item.value = value; 2205 } 2206} 2207 2208@Component 2209export default struct MyComponent { 2210 public controller: MyComponentController | null = null; 2211 @State value: CC = new CC('Hello World') 2212 2213 build() { 2214 Column() { 2215 Text(`${this.value.value}`) 2216 .fontSize(50) 2217 } 2218 } 2219 2220 aboutToAppear() { 2221 if (this.controller) 2222 this.controller.setItem(this.value); 2223 } 2224} 2225 2226@Entry 2227@Component 2228struct StyleExample { 2229 controller: MyComponentController = new MyComponentController(); 2230 2231 build() { 2232 Column() { 2233 MyComponent({ controller: this.controller }) 2234 Button('change value').onClick(() => { 2235 this.controller.changeText('Text') 2236 }) 2237 } 2238 } 2239} 2240``` 2241 2242### Struct支持联合类型的方案 2243 2244下面这段代码有arkts-no-any-unknown的报错,由于strcut不支持泛型,建议使用联合类型,实现自定义组件类似泛型的功能。 2245 2246**不推荐用法** 2247 2248```typescript 2249class Data { 2250 aa: number = 11; 2251} 2252 2253@Entry 2254@Component 2255struct DatauionOldPage { 2256 @State array: Data[] = [new Data(), new Data(), new Data()]; 2257 2258 @Builder 2259 componentCloser(data: Data) { 2260 Text(data.aa + '').fontSize(50) 2261 } 2262 2263 build() { 2264 Row() { 2265 Column() { 2266 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2267 } 2268 .width('100%') 2269 } 2270 .height('100%') 2271 } 2272} 2273 2274@Component 2275export struct ForEachCom { 2276 arrayList: any[] 2277 @BuilderParam closer: (data: any) => void = this.componentCloser 2278 2279 @Builder 2280 componentCloser() { 2281 } 2282 2283 build() { 2284 Column() { 2285 ForEach(this.arrayList, (item: any) => { 2286 Row() { 2287 this.closer(item) 2288 }.width('100%').height(200).backgroundColor('#eee') 2289 }) 2290 } 2291 } 2292} 2293``` 2294 2295**推荐用法** 2296 2297```typescript 2298class Data { 2299 aa: number = 11; 2300} 2301 2302class Model { 2303 aa: string = '11'; 2304} 2305 2306type UnionData = Data | Model 2307 2308@Entry 2309@Component 2310struct DatauionPage { 2311 array: UnionData[] = [new Data(), new Data(), new Data()]; 2312 2313 @Builder 2314 componentCloser(data: UnionData) { 2315 if (data instanceof Data) { 2316 Text(data.aa + '').fontSize(50) 2317 } 2318 } 2319 2320 build() { 2321 Row() { 2322 Column() { 2323 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2324 } 2325 .width('100%') 2326 } 2327 .height('100%') 2328 } 2329} 2330 2331@Component 2332export struct ForEachCom { 2333 arrayList: UnionData[] = [new Data(), new Data(), new Data()]; 2334 @BuilderParam closer: (data: UnionData) => void = this.componentCloser 2335 2336 @Builder 2337 componentCloser() { 2338 } 2339 2340 build() { 2341 Column() { 2342 ForEach(this.arrayList, (item: UnionData) => { 2343 Row() { 2344 this.closer(item) 2345 }.width('100%').height(200).backgroundColor('#eee') 2346 }) 2347 } 2348 } 2349} 2350```