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 { router } from '@kit.ArkUI'; 357let params: Object = router.getParams(); 358let funNum: number = params['funNum']; 359let target: string = params['target']; 360``` 361 362**建议改法** 363 364```typescript 365import { router } from '@kit.ArkUI'; 366let params = router.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 '@kit.ImageKit'; 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 663export default { 664 onCreate() { 665 // ... 666 }, 667 onDestroy() { 668 // ... 669 } 670} 671``` 672 673**建议改法** 674 675```typescript 676class Test { 677 onCreate() { 678 // ... 679 } 680 onDestroy() { 681 // ... 682 } 683} 684 685export default new Test() 686``` 687 688### 通过导入namespace获取类型 689 690**应用代码** 691 692```typescript 693// test.d.ets 694declare namespace test { 695 interface I { 696 id: string; 697 type: number; 698 } 699 700 function foo(name: string, option: I): void; 701} 702 703export default test; 704 705// app.ets 706import { test } from 'test'; 707 708let option = { id: '', type: 0 }; 709test.foo('', option); 710``` 711 712**建议改法** 713 714```typescript 715// test.d.ets 716declare namespace test { 717 interface I { 718 id: string; 719 type: number; 720 } 721 722 function foo(name: string, option: I): void; 723} 724 725export default test; 726 727// app.ets 728import { test } from 'test'; 729 730let option: test.I = { id: '', type: 0 }; 731test.foo('', option); 732``` 733 734**原因** 735 736对象字面量缺少类型,根据`test.foo`分析可以得知,`option`的类型来源于声明文件,那么只需要将类型导入即可。 737注意到在`test.d.ets`中,`I`是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。 738 739### object literal传参给Object类型 740 741**应用代码** 742 743```typescript 744function emit(event: string, ...args: Object[]): void {} 745 746emit('', { 747 'action': 11, 748 'outers': false 749}); 750``` 751 752**建议改法** 753 754```typescript 755function emit(event: string, ...args: Object[]): void {} 756 757let emitArg: Record<string, number | boolean> = { 758 'action': 11, 759 'outers': false 760} 761 762emit('', emitArg); 763``` 764 765## arkts-no-obj-literals-as-types 766 767**应用代码** 768 769```typescript 770type Person = { name: string, age: number } 771``` 772 773**建议改法** 774 775```typescript 776interface Person { 777 name: string, 778 age: number 779} 780``` 781 782## arkts-no-noninferrable-arr-literals 783 784**应用代码** 785 786```typescript 787let permissionList = [ 788 { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' }, 789 { name: '麦克风', value: '用于反馈问题单时增加语音' }, 790 { name: '存储', value: '用于反馈问题单时增加本地文件附件' } 791] 792``` 793 794**建议改法** 795 796为对象字面量声明类型 797 798```typescript 799class PermissionItem { 800 name?: string 801 value?: string 802} 803 804let permissionList: PermissionItem[] = [ 805 { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' }, 806 { name: '麦克风', value: '用于反馈问题单时增加语音' }, 807 { name: '存储', value: '用于反馈问题单时增加本地文件附件' } 808] 809``` 810 811## arkts-no-method-reassignment 812 813**应用代码** 814 815```typescript 816class C { 817 add(left: number, right: number): number { 818 return left + right; 819 } 820} 821 822function sub(left: number, right: number): number { 823 return left - right; 824} 825 826let c1 = new C(); 827c1.add = sub; 828``` 829 830**建议改法** 831 832```typescript 833class C { 834 add: (left: number, right: number) => number = 835 (left: number, right: number) => { 836 return left + right; 837 } 838} 839 840function sub(left: number, right: number): number { 841 return left - right; 842} 843 844let c1 = new C(); 845c1.add = sub; 846``` 847 848## arkts-no-polymorphic-unops 849 850**应用代码** 851 852```typescript 853let a = +'5'; 854let b = -'5'; 855let c = ~'5'; 856let d = +'string'; 857``` 858 859**建议改法** 860 861```typescript 862let a = Number.parseInt('5'); 863let b = -Number.parseInt('5'); 864let c = ~Number.parseInt('5'); 865let d = new Number('string'); 866``` 867 868## arkts-no-type-query 869 870**应用代码** 871 872```typescript 873// module1.ts 874class C { 875 value: number = 0 876} 877 878export let c = new C() 879 880// module2.ts 881import { c } from './module1' 882let t: typeof c = { value: 123 }; 883``` 884 885**建议改法** 886 887```typescript 888// module1.ts 889class C { 890 value: number = 0 891} 892 893export { C } 894 895// module2.ts 896import { C } from './module1' 897let t: C = { value: 123 }; 898``` 899 900## arkts-no-in 901 902### 使用Object.keys判断属性是否存在 903 904**应用代码** 905 906```typescript 907function test(str: string, obj: Record<string, Object>) { 908 return str in obj; 909} 910``` 911 912**建议改法** 913 914```typescript 915function test(str: string, obj: Record<string, Object>) { 916 for (let i of Object.keys(obj)) { 917 if (i == str) { 918 return true; 919 } 920 } 921 return false; 922} 923``` 924 925## arkts-no-destruct-assignment 926 927**应用代码** 928 929```typescript 930let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 931for (let [key, value] of map) { 932 console.log(key); 933 console.log(value); 934} 935``` 936 937**建议改法** 938 939使用数组 940 941```typescript 942let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 943for (let arr of map) { 944 let key = arr[0]; 945 let value = arr[1]; 946 console.log(key); 947 console.log(value); 948} 949``` 950 951## arkts-no-types-in-catch 952 953**应用代码** 954 955```typescript 956import { BusinessError } from '@kit.BasicServicesKit' 957 958try { 959 // ... 960} catch (e: BusinessError) { 961 console.error(e.message, e.code); 962} 963``` 964 965**建议改法** 966 967```typescript 968import { BusinessError } from '@kit.BasicServicesKit' 969 970try { 971 // ... 972} catch (error) { 973 let e: BusinessError = error as BusinessError; 974 console.error(e.message, e.code); 975} 976``` 977 978## arkts-no-for-in 979 980**应用代码** 981 982```typescript 983interface Person { 984 [name: string]: string 985} 986let p: Person = { 987 name: 'tom', 988 age: '18' 989}; 990 991for (let t in p) { 992 console.log(p[t]); // log: "tom", "18" 993} 994``` 995 996**建议改法** 997 998```typescript 999let p: Record<string, string> = { 1000 'name': 'tom', 1001 'age': '18' 1002}; 1003 1004for (let ele of Object.entries(p)) { 1005 console.log(ele[1]); // log: "tom", "18" 1006} 1007``` 1008 1009## arkts-no-mapped-types 1010 1011**应用代码** 1012 1013```typescript 1014class C { 1015 a: number = 0 1016 b: number = 0 1017 c: number = 0 1018} 1019type OptionsFlags = { 1020 [Property in keyof C]: string 1021} 1022``` 1023 1024**建议改法** 1025 1026```typescript 1027class C { 1028 a: number = 0 1029 b: number = 0 1030 c: number = 0 1031} 1032 1033type OptionsFlags = Record<keyof C, string> 1034``` 1035 1036## arkts-limited-throw 1037 1038**应用代码** 1039 1040```typescript 1041import { BusinessError } from '@kit.BasicServicesKit' 1042 1043function ThrowError(error: BusinessError) { 1044 throw error; 1045} 1046``` 1047 1048**建议改法** 1049 1050```typescript 1051import { BusinessError } from '@kit.BasicServicesKit' 1052 1053function ThrowError(error: BusinessError) { 1054 throw error as Error; 1055} 1056``` 1057 1058**原因** 1059 1060`throw`语句中值的类型必须为`Error`或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用`as`将类型转换为`Error`。 1061 1062## arkts-no-standalone-this 1063 1064### 函数内使用this 1065 1066**应用代码** 1067 1068```typescript 1069function foo() { 1070 console.log(this.value); 1071} 1072 1073let obj = { value: 'abc' }; 1074foo.apply(obj); 1075``` 1076 1077**建议改法1** 1078 1079使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制 1080 1081```typescript 1082class Test { 1083 value: string = '' 1084 constructor (value: string) { 1085 this.value = value 1086 } 1087 1088 foo() { 1089 console.log(this.value); 1090 } 1091} 1092 1093let obj: Test = new Test('abc'); 1094obj.foo(); 1095``` 1096 1097**建议改法2** 1098 1099将this作为参数传入 1100 1101```typescript 1102function foo(obj: Test) { 1103 console.log(obj.value); 1104} 1105 1106class Test { 1107 value: string = '' 1108} 1109 1110let obj: Test = { value: 'abc' }; 1111foo(obj); 1112``` 1113 1114**建议改法3** 1115 1116将属性作为参数传入 1117```typescript 1118function foo(value: string) { 1119 console.log(value); 1120} 1121 1122class Test { 1123 value: string = '' 1124} 1125 1126let obj: Test = { value: 'abc' }; 1127foo(obj.value); 1128``` 1129 1130### class的静态方法内使用this 1131 1132**应用代码** 1133 1134```typescript 1135class Test { 1136 static value: number = 123 1137 static foo(): number { 1138 return this.value 1139 } 1140} 1141``` 1142 1143**建议改法** 1144 1145```typescript 1146class Test { 1147 static value: number = 123 1148 static foo(): number { 1149 return Test.value 1150 } 1151} 1152``` 1153 1154## arkts-no-spread 1155 1156**应用代码** 1157 1158```typescript 1159// test.d.ets 1160declare namespace test { 1161 interface I { 1162 id: string; 1163 type: number; 1164 } 1165 1166 function foo(): I; 1167} 1168 1169export default test 1170 1171// app.ets 1172import test from 'test'; 1173 1174let t: test.I = { 1175 ...test.foo(), 1176 type: 0 1177} 1178``` 1179 1180**建议改法** 1181 1182```typescript 1183// test.d.ets 1184declare namespace test { 1185 interface I { 1186 id: string; 1187 type: number; 1188 } 1189 1190 function foo(): I; 1191} 1192 1193export default test 1194 1195// app.ets 1196import test from 'test'; 1197 1198let t: test.I = test.foo(); 1199t.type = 0; 1200``` 1201 1202**原因** 1203 1204ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。 1205 1206## arkts-no-ctor-signatures-funcs 1207 1208在class内声明属性,而不是在构造函数上。 1209 1210**应用代码** 1211 1212```typescript 1213class Controller { 1214 value: string = '' 1215 constructor(value: string) { 1216 this.value = value 1217 } 1218} 1219 1220type ControllerConstructor = new (value: string) => Controller; 1221 1222class Menu { 1223 controller: ControllerConstructor = Controller 1224 createController() { 1225 if (this.controller) { 1226 return new this.controller('abc'); 1227 } 1228 return null; 1229 } 1230} 1231 1232let t = new Menu() 1233console.log(t.createController()!.value) 1234``` 1235 1236**建议改法** 1237 1238```typescript 1239class Controller { 1240 value: string = '' 1241 constructor(value: string) { 1242 this.value = value; 1243 } 1244} 1245 1246type ControllerConstructor = () => Controller; 1247 1248class Menu { 1249 controller: ControllerConstructor = () => { return new Controller('abc') } 1250 createController() { 1251 if (this.controller) { 1252 return this.controller(); 1253 } 1254 return null; 1255 } 1256} 1257 1258let t: Menu = new Menu(); 1259console.log(t.createController()!.value); 1260``` 1261 1262## arkts-no-globalthis 1263 1264由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。 1265 12661. 建议按照业务逻辑根据`import/export`语法实现数据在不同模块的传递。 1267 12682. 必要情况下,可以通过构造的**单例对象**来实现全局对象的功能。(**说明:**不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。) 1269 1270**构造单例对象** 1271 1272```typescript 1273// 构造单例对象 1274export class GlobalContext { 1275 private constructor() {} 1276 private static instance: GlobalContext; 1277 private _objects = new Map<string, Object>(); 1278 1279 public static getContext(): GlobalContext { 1280 if (!GlobalContext.instance) { 1281 GlobalContext.instance = new GlobalContext(); 1282 } 1283 return GlobalContext.instance; 1284 } 1285 1286 getObject(value: string): Object | undefined { 1287 return this._objects.get(value); 1288 } 1289 1290 setObject(key: string, objectClass: Object): void { 1291 this._objects.set(key, objectClass); 1292 } 1293} 1294 1295``` 1296 1297**应用代码** 1298 1299```typescript 1300 1301// file1.ts 1302 1303export class Test { 1304 value: string = ''; 1305 foo(): void { 1306 globalThis.value = this.value; 1307 } 1308} 1309 1310// file2.ts 1311 1312globalThis.value; 1313 1314``` 1315 1316**建议改法** 1317 1318```typescript 1319 1320// file1.ts 1321 1322import { GlobalContext } from '../GlobalContext' 1323 1324export class Test { 1325 value: string = ''; 1326 foo(): void { 1327 GlobalContext.getContext().setObject('value', this.value); 1328 } 1329} 1330 1331// file2.ts 1332 1333import { GlobalContext } from '../GlobalContext' 1334 1335GlobalContext.getContext().getObject('value'); 1336``` 1337 1338## arkts-no-func-apply-bind-call 1339 1340### 使用标准库中接口 1341 1342**应用代码** 1343 1344```typescript 1345let arr: number[] = [1, 2, 3, 4]; 1346let str = String.fromCharCode.apply(null, Array.from(arr)); 1347``` 1348 1349**建议改法** 1350 1351```typescript 1352let arr: number[] = [1, 2, 3, 4]; 1353let str = String.fromCharCode(...Array.from(arr)); 1354``` 1355 1356### bind定义方法 1357 1358**应用代码** 1359 1360```typescript 1361class A { 1362 value: string = '' 1363 foo: Function = () => {} 1364} 1365 1366class Test { 1367 value: string = '1234' 1368 obj: A = { 1369 value: this.value, 1370 foo: this.foo.bind(this) 1371 } 1372 1373 foo() { 1374 console.log(this.value); 1375 } 1376} 1377``` 1378 1379**建议改法1** 1380 1381```typescript 1382class A { 1383 value: string = '' 1384 foo: Function = () => {} 1385} 1386 1387class Test { 1388 value: string = '1234' 1389 obj: A = { 1390 value: this.value, 1391 foo: (): void => this.foo() 1392 } 1393 1394 foo() { 1395 console.log(this.value); 1396 } 1397} 1398``` 1399 1400**建议改法2** 1401 1402```typescript 1403class A { 1404 value: string = '' 1405 foo: Function = () => {} 1406} 1407 1408class Test { 1409 value: string = '1234' 1410 foo: () => void = () => { 1411 console.log(this.value); 1412 } 1413 obj: A = { 1414 value: this.value, 1415 foo: this.foo 1416 } 1417} 1418``` 1419 1420### 使用apply 1421 1422**应用代码** 1423 1424```typescript 1425class A { 1426 value: string; 1427 constructor (value: string) { 1428 this.value = value; 1429 } 1430 1431 foo() { 1432 console.log(this.value); 1433 } 1434} 1435 1436let a1 = new A('1'); 1437let a2 = new A('2'); 1438 1439a1.foo(); 1440a1.foo.apply(a2); 1441``` 1442 1443**建议改法** 1444 1445```typescript 1446class A { 1447 value: string; 1448 constructor (value: string) { 1449 this.value = value; 1450 } 1451 1452 foo() { 1453 this.fooApply(this); 1454 } 1455 1456 fooApply(a: A) { 1457 console.log(a.value); 1458 } 1459} 1460 1461let a1 = new A('1'); 1462let a2 = new A('2'); 1463 1464a1.foo(); 1465a1.fooApply(a2); 1466``` 1467 1468## arkts-limited-stdlib 1469 1470### `Object.fromEntries()` 1471 1472**应用代码** 1473 1474```typescript 1475let entries = new Map([ 1476 ['foo', 123], 1477 ['bar', 456] 1478]); 1479 1480let obj = Object.fromEntries(entries); 1481``` 1482 1483**建议改法** 1484 1485```typescript 1486let entries = new Map([ 1487 ['foo', 123], 1488 ['bar', 456] 1489]); 1490 1491let obj: Record<string, Object> = {}; 1492entries.forEach((value, key) => { 1493 if (key != undefined && key != null) { 1494 obj[key] = value; 1495 } 1496}) 1497``` 1498 1499### 使用`Number`的属性和方法 1500 1501ArkTS不允许使用全局对象的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt` 1502 1503可以使用`Number`的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt` 1504 1505**应用代码** 1506 1507```typescript 1508NaN; 1509isFinite(123); 1510parseInt('123'); 1511``` 1512 1513**建议改法** 1514 1515```typescript 1516Number.NaN; 1517Number.isFinite(123); 1518Number.parseInt('123'); 1519``` 1520 1521## arkts-strict-typing(StrictModeError) 1522 1523### strictPropertyInitialization 1524 1525**应用代码** 1526 1527```typescript 1528interface I { 1529 name:string 1530} 1531 1532class A {} 1533 1534class Test { 1535 a: number; 1536 b: string; 1537 c: boolean; 1538 d: I; 1539 e: A; 1540} 1541 1542``` 1543 1544**建议改法** 1545 1546```typescript 1547interface I { 1548 name:string 1549} 1550 1551class A {} 1552 1553class Test { 1554 a: number; 1555 b: string; 1556 c: boolean; 1557 d: I = { name:'abc' }; 1558 e: A | null = null; 1559 constructor(a:number, b:string, c:boolean) { 1560 this.a = a; 1561 this.b = b; 1562 this.c = c; 1563 } 1564} 1565 1566``` 1567### Type `*** | null` is not assignable to type `***` 1568 1569**应用代码** 1570 1571```typescript 1572class A { 1573 bar() {} 1574} 1575function foo(n: number) { 1576 if (n === 0) { 1577 return null; 1578 } 1579 return new A(); 1580} 1581function getNumber() { 1582 return 5; 1583} 1584let a:A = foo(getNumber()); 1585a.bar(); 1586``` 1587 1588**建议改法** 1589 1590```typescript 1591class A { 1592 bar() {} 1593} 1594function foo(n: number) { 1595 if (n === 0) { 1596 return null; 1597 } 1598 return new A(); 1599} 1600function getNumber() { 1601 return 5; 1602} 1603 1604let a: A | null = foo(getNumber()); 1605a?.bar(); 1606``` 1607 1608### 严格属性初始化检查 1609 1610在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。 1611 1612**建议改法** 1613 16141.一般情况下,**建议按照业务逻辑**在声明时初始化属性,或者在构造函数中为属性赋值。如: 1615 1616```typescript 1617//code with error 1618class Test { 1619 value: number 1620 flag: boolean 1621} 1622 1623//方式一,在声明时初始化 1624class Test { 1625 value: number = 0 1626 flag: boolean = false 1627} 1628 1629//方式二,在构造函数中赋值 1630class Test { 1631 value: number 1632 flag: boolean 1633 constructor(value: number, flag: boolean) { 1634 this.value = value; 1635 this.flag = flag; 1636 } 1637} 1638``` 1639 16402.对于对象类型(包括函数类型)`A`,如果不确定如何初始化,建议按照以下方式之一进行初始化 1641 1642 方式(i) `prop: A | null = null` 1643 1644 方式(ii) `prop?: A` 1645 1646 方式三(iii) `prop: A | undefined = undefined` 1647 1648- 从性能角度来说,`null`类型只用在编译期的类型检查中,对虚拟机的性能无影响。而`undefined | A`被视为联合类型,运行时可能有额外的开销。 1649- 从代码可读性、简洁性的角度来说,`prop?:A`是`prop: A | undefined = undefined`的语法糖,**推荐使用可选属性的写法** 1650 1651### 严格函数类型检查 1652 1653**应用代码** 1654 1655```typescript 1656function foo(fn: (value?: string) => void, value: string): void {} 1657 1658foo((value: string) => {}, ''); //error 1659``` 1660 1661**建议改法** 1662 1663```typescript 1664function foo(fn: (value?: string) => void, value: string): void {} 1665 1666foo((value?: string) => {}, ''); 1667``` 1668 1669**原因** 1670 1671例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在`foo`的函数体中,一个`undefined`被传入`fn`(这是可以的,因为`fn`可以接受`undefined`),但是在代码第6行`foo`的调用点,传入的`(value: string) => { console.log(value.toUpperCase()) }`的函数实现中,始终将参数`value`当做string类型,允许其调用`toUpperCase`方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在`undefined`上无法找到属性的错误。 1672 1673```typescript 1674function foo(fn: (value?: string) => void, value: string): void { 1675 let v: string | undefined = undefined; 1676 fn(v); 1677} 1678 1679foo((value: string) => { console.log(value.toUpperCase()) }, ''); // Cannot read properties of undefined (reading 'toUpperCase') 1680``` 1681 1682为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。 1683 1684### 严格空值检查 1685 1686**应用代码** 1687 1688```typescript 1689class Test { 1690 private value?: string 1691 1692 public printValue () { 1693 console.log(this.value.toLowerCase()); 1694 } 1695} 1696 1697let t = new Test(); 1698t.printValue(); 1699``` 1700 1701**建议改法** 1702 1703在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。 1704 1705```typescript 1706class Test { 1707 private value?: string 1708 1709 public printValue () { 1710 if (this.value) { 1711 console.log(this.value.toLowerCase()); 1712 } 1713 } 1714} 1715 1716let t = new Test(); 1717t.printValue(); 1718``` 1719 1720**原因** 1721 1722在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为`t`的属性`value`为`undefined`(这是因为`value?: string`是`value: string | undefined = undefined`的语法糖),在第11行调用`printValue`方法时,由于在该方法体内未对`this.value`的值进行空值检查,而直接按照`string`类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。 1723 1724### 函数返回类型不匹配 1725 1726**应用代码** 1727 1728```typescript 1729class Test { 1730 handleClick: (action: string, externInfo?: string) => void | null = null; 1731} 1732``` 1733 1734**建议改法** 1735 1736在这种写法下,函数返回类型被解析为 `void | undefined`,需要添加括号用来区分union类型。 1737 1738```typescript 1739class Test { 1740 handleClick: ((action: string, externInfo?: string) => void) | null = null; 1741} 1742``` 1743 1744### '***' is of type 'unknown' 1745 1746**应用代码** 1747 1748```typescript 1749try { 1750 1751} catch (error) { 1752 console.log(error.message); 1753} 1754``` 1755 1756**建议改法** 1757 1758```typescript 1759import { BusinessError } from '@kit.BasicServicesKit' 1760 1761try { 1762 1763} catch (error) { 1764 console.log((error as BusinessError).message); 1765} 1766``` 1767 1768### Type '*** | null' is not assignable to type '\*\*\*' 1769 1770**应用代码** 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 = foo(); 1788``` 1789 1790**建议改法1** 1791 1792修改变量`a`的类型:`let a: A | null = foo()`。 1793 1794```typescript 1795class A { 1796 value: number 1797 constructor(value: number) { 1798 this.value = value; 1799 } 1800} 1801 1802function foo(v: number): A | null { 1803 if (v > 0) { 1804 return new A(v); 1805 } 1806 return null; 1807} 1808 1809let a: A | null = foo(123); 1810 1811if (a != null) { 1812 // 非空分支 1813} else { 1814 // 处理null 1815} 1816``` 1817 1818**建议改法2** 1819 1820如果可以断定此处调用`foo`一定返回非空值,可以使用非空断言`!`。 1821 1822```typescript 1823class A { 1824 value: number 1825 constructor(value: number) { 1826 this.value = value; 1827 } 1828} 1829 1830function foo(v: number): A | null { 1831 if (v > 0) { 1832 return new A(v); 1833 } 1834 return null; 1835} 1836 1837let a: A = foo(123)!; 1838``` 1839 1840### Cannot invoke an object which possibly 'undefined' 1841 1842**应用代码** 1843 1844```typescript 1845interface A { 1846 foo?: () => void 1847} 1848 1849let a:A = { foo: () => {} }; 1850a.foo(); 1851``` 1852 1853**建议改法1** 1854 1855```typescript 1856interface A { 1857 foo: () => void 1858} 1859let a: A = { foo: () => {} }; 1860a.foo(); 1861``` 1862 1863**建议改法2** 1864 1865```typescript 1866interface A { 1867 foo?: () => void 1868} 1869 1870let a: A = { foo: () => {} }; 1871if (a.foo) { 1872 a.foo(); 1873} 1874``` 1875 1876**原因** 1877 1878在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。 1879 1880### Variable '***' is used before being assigned 1881 1882**应用代码** 1883 1884```typescript 1885class Test { 1886 value: number = 0 1887} 1888 1889let a: Test 1890try { 1891 a = { value: 1}; 1892} catch (e) { 1893 a.value; 1894} 1895a.value; 1896``` 1897 1898**建议改法** 1899 1900```typescript 1901class Test { 1902 value: number = 0 1903} 1904 1905let a: Test | null = null; 1906try { 1907 a = { value:1 }; 1908} catch (e) { 1909 if (a) { 1910 a.value; 1911 } 1912} 1913 1914if (a) { 1915 a.value; 1916} 1917``` 1918 1919**原因** 1920 1921对于primitive types,可以根据业务逻辑赋值,例如0,'',false。 1922 1923对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。 1924 1925### Function lacks ending return statement and return type does not include 'undefined'. 1926 1927**应用代码** 1928 1929```typescript 1930function foo(a: number): number { 1931 if (a > 0) { 1932 return a; 1933 } 1934} 1935``` 1936 1937**建议改法1** 1938 1939根据业务逻辑,在else分支中返回合适的数值 1940 1941**建议改法2** 1942 1943```typescript 1944function foo(a: number): number | undefined { 1945 if (a > 0) { 1946 return a; 1947 } 1948 return 1949} 1950``` 1951 1952## arkts-strict-typing-required 1953 1954**应用代码** 1955 1956```typescript 1957// @ts-nocheck 1958var a: any = 123; 1959``` 1960 1961**建议改法** 1962 1963```typescript 1964let a: number = 123; 1965``` 1966 1967**原因** 1968 1969ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(`// @ts-nocheck`或者`// @ts-ignore`)删去,再根据报错信息修改其他代码。 1970 1971## Importing ArkTS files to JS and TS files is not allowed 1972 1973## arkts-no-tsdeps 1974 1975不允许.ts、.js文件`import`.ets文件源码。 1976 1977**建议改法** 1978 1979方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。 1980 1981方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。 1982 1983## arkts-no-special-imports 1984 1985**应用代码** 1986 1987```typescript 1988import type {A, B, C, D } from '***' 1989``` 1990 1991 1992**建议改法** 1993 1994```typescript 1995import {A, B, C, D } from '***' 1996``` 1997 1998## arkts-no-classes-as-obj 1999 2000### 使用class构造实例 2001 2002**应用代码** 2003 2004```typescript 2005class Controller { 2006 value: string = '' 2007 constructor(value: string) { 2008 this.value = value 2009 } 2010} 2011 2012interface ControllerConstructor { 2013 new (value: string): Controller; 2014} 2015 2016class Menu { 2017 controller: ControllerConstructor = Controller 2018 createController() { 2019 if (this.controller) { 2020 return new this.controller('abc'); 2021 } 2022 return null; 2023 } 2024} 2025 2026let t = new Menu(); 2027console.log(t.createController()!.value); 2028``` 2029 2030**建议改法** 2031 2032```typescript 2033class Controller { 2034 value: string = '' 2035 constructor(value: string) { 2036 this.value = value 2037 } 2038} 2039 2040type ControllerConstructor = () => Controller; 2041 2042class Menu { 2043 controller: ControllerConstructor = () => { return new Controller('abc'); } 2044 createController() { 2045 if (this.controller) { 2046 return this.controller(); 2047 } 2048 return null; 2049 } 2050} 2051 2052let t: Menu = new Menu(); 2053console.log(t.createController()!.value); 2054``` 2055 2056### 访问静态属性 2057 2058**应用代码** 2059 2060```typescript 2061class C1 { 2062 static value: string = 'abc' 2063} 2064 2065class C2 { 2066 static value: string = 'def' 2067} 2068 2069function getValue(obj: any) { 2070 return obj['value']; 2071} 2072 2073console.log(getValue(C1)); 2074console.log(getValue(C2)); 2075``` 2076 2077**建议改法** 2078 2079```typescript 2080class C1 { 2081 static value: string = 'abc' 2082} 2083 2084class C2 { 2085 static value: string = 'def' 2086} 2087 2088function getC1Value(): string { 2089 return C1.value; 2090} 2091 2092function getC2Value(): string { 2093 return C2.value; 2094} 2095 2096console.log(getC1Value()); 2097console.log(getC2Value()); 2098``` 2099 2100## arkts-no-side-effects-imports 2101 2102改用动态import 2103 2104**应用代码** 2105 2106```typescript 2107import 'module' 2108``` 2109 2110**建议改法** 2111 2112```typescript 2113import('module') 2114``` 2115 2116## arkts-no-func-props 2117 2118**应用代码** 2119 2120```typescript 2121function foo(value: number): void { 2122 console.log(value.toString()); 2123} 2124 2125foo.add = (left: number, right: number) => { 2126 return left + right; 2127} 2128 2129foo.sub = (left: number, right: number) => { 2130 return left - right; 2131} 2132``` 2133 2134**建议改法** 2135 2136```typescript 2137class Foo { 2138 static foo(value: number): void { 2139 console.log(value.toString()); 2140 } 2141 2142 static add(left: number, right: number): number { 2143 return left + right; 2144 } 2145 2146 static sub(left: number, right: number): number { 2147 return left - right; 2148 } 2149} 2150``` 2151 2152## arkts-limited-esobj 2153 2154**应用代码** 2155 2156```typescript 2157// lib.d.ts 2158declare function foo(): any; 2159 2160// main.ets 2161let e0: ESObject = foo(); 2162 2163function f() { 2164 let e1 = foo(); 2165 let e2: ESObject = 1; 2166 let e3: ESObject = {}; 2167 let e4: ESObject = ''; 2168} 2169``` 2170 2171**建议改法** 2172 2173```typescript 2174// lib.d.ts 2175declare function foo(): any; 2176 2177// main.ets 2178interface I {} 2179 2180function f() { 2181 let e0: ESObject = foo(); 2182 let e1: ESObject = foo(); 2183 let e2: number = 1; 2184 let e3: I = {}; 2185 let e4: string = ''; 2186} 2187``` 2188 2189## 拷贝 2190 2191### 浅拷贝 2192 2193**TypeScript** 2194 2195```typescript 2196function shallowCopy(obj: object): object { 2197 let newObj = {}; 2198 Object.assign(newObj, obj); 2199 return newObj; 2200} 2201``` 2202 2203**ArkTS** 2204 2205```typescript 2206function shallowCopy(obj: object): object { 2207 let newObj: Record<string, Object> = {}; 2208 for (let key of Object.keys(obj)) { 2209 newObj[key] = obj[key]; 2210 } 2211 return newObj; 2212} 2213``` 2214 2215### 深拷贝 2216 2217**TypeScript** 2218 2219```typescript 2220function deepCopy(obj: object): object { 2221 let newObj = Array.isArray(obj) ? [] : {}; 2222 for (let key in obj) { 2223 if (typeof obj[key] === 'object') { 2224 newObj[key] = deepCopy(obj[key]); 2225 } else { 2226 newObj[key] = obj[key]; 2227 } 2228 } 2229 return newObj; 2230} 2231``` 2232 2233**ArkTS** 2234 2235```typescript 2236function deepCopy(obj: object): object { 2237 let newObj: Record<string, Object> | Object[] = Array.isArray(obj) ? [] : {}; 2238 for (let key of Object.keys(obj)) { 2239 if (typeof obj[key] === 'object') { 2240 newObj[key] = deepCopy(obj[key]); 2241 } else { 2242 newObj[key] = obj[key]; 2243 } 2244 } 2245 return newObj; 2246} 2247``` 2248 2249## 状态管理使用典型场景 2250 2251### Struct组件外使用状态变量 2252 2253由于struct和class不同,不建议把this作为参数传递到struct外部使用,避免引起实例引用无法释放的情况,导致内存泄露。建议将状态变量对象传递到struct外面使用,通过修改对象的属性,来触发UI刷新。 2254 2255**不推荐用法** 2256 2257```typescript 2258export class MyComponentController { 2259 item: MyComponent = null; 2260 2261 setItem(item: MyComponent) { 2262 this.item = item; 2263 } 2264 2265 changeText(value: string) { 2266 this.item.value = value; 2267 } 2268} 2269 2270@Component 2271export default struct MyComponent { 2272 public controller: MyComponentController = null; 2273 @State value: string = 'Hello World'; 2274 2275 build() { 2276 Column() { 2277 Text(this.value) 2278 .fontSize(50) 2279 } 2280 } 2281 2282 aboutToAppear() { 2283 if (this.controller) 2284 this.controller.setItem(this); // 不建议把this作为参数传递到struct外部使用 2285 } 2286} 2287 2288@Entry 2289@Component 2290struct ObjThisOldPage { 2291 controller = new MyComponentController(); 2292 2293 build() { 2294 Column() { 2295 MyComponent({ controller: this.controller }) 2296 Button('change value').onClick(() => { 2297 this.controller.changeText('Text'); 2298 }) 2299 } 2300 } 2301} 2302``` 2303 2304**推荐用法** 2305 2306```typescript 2307class CC { 2308 value: string = '1'; 2309 2310 constructor(value: string) { 2311 this.value = value; 2312 } 2313} 2314 2315export class MyComponentController { 2316 item: CC = new CC('1'); 2317 2318 setItem(item: CC) { 2319 this.item = item; 2320 } 2321 2322 changeText(value: string) { 2323 this.item.value = value; 2324 } 2325} 2326 2327@Component 2328export default struct MyComponent { 2329 public controller: MyComponentController | null = null; 2330 @State value: CC = new CC('Hello World'); 2331 2332 build() { 2333 Column() { 2334 Text(`${this.value.value}`) 2335 .fontSize(50) 2336 } 2337 } 2338 2339 aboutToAppear() { 2340 if (this.controller) 2341 this.controller.setItem(this.value); 2342 } 2343} 2344 2345@Entry 2346@Component 2347struct StyleExample { 2348 controller: MyComponentController = new MyComponentController(); 2349 2350 build() { 2351 Column() { 2352 MyComponent({ controller: this.controller }) 2353 Button('change value').onClick(() => { 2354 this.controller.changeText('Text'); 2355 }) 2356 } 2357 } 2358} 2359``` 2360 2361### Struct支持联合类型的方案 2362 2363下面这段代码有arkts-no-any-unknown的报错,由于struct不支持泛型,建议使用联合类型,实现自定义组件类似泛型的功能。 2364 2365**不推荐用法** 2366 2367```typescript 2368class Data { 2369 aa: number = 11; 2370} 2371 2372@Entry 2373@Component 2374struct DatauionOldPage { 2375 @State array: Data[] = [new Data(), new Data(), new Data()]; 2376 2377 @Builder 2378 componentCloser(data: Data) { 2379 Text(data.aa + '').fontSize(50) 2380 } 2381 2382 build() { 2383 Row() { 2384 Column() { 2385 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2386 } 2387 .width('100%') 2388 } 2389 .height('100%') 2390 } 2391} 2392 2393@Component 2394export struct ForEachCom { 2395 arrayList: any[]; // struct不支持泛型,有arkts-no-any-unknown报错 2396 @BuilderParam closer: (data: any) => void = this.componentCloser; // struct不支持泛型,有arkts-no-any-unknown报错 2397 2398 @Builder 2399 componentCloser() { 2400 } 2401 2402 build() { 2403 Column() { 2404 ForEach(this.arrayList, (item: any) => { // struct不支持泛型,有arkts-no-any-unknown报错 2405 Row() { 2406 this.closer(item) 2407 }.width('100%').height(200).backgroundColor('#eee') 2408 }) 2409 } 2410 } 2411} 2412``` 2413 2414**推荐用法** 2415 2416```typescript 2417class Data { 2418 aa: number = 11; 2419} 2420 2421class Model { 2422 aa: string = '11'; 2423} 2424 2425type UnionData = Data | Model; 2426 2427@Entry 2428@Component 2429struct DatauionPage { 2430 array: UnionData[] = [new Data(), new Data(), new Data()]; 2431 2432 @Builder 2433 componentCloser(data: UnionData) { 2434 if (data instanceof Data) { 2435 Text(data.aa + '').fontSize(50) 2436 } 2437 } 2438 2439 build() { 2440 Row() { 2441 Column() { 2442 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2443 } 2444 .width('100%') 2445 } 2446 .height('100%') 2447 } 2448} 2449 2450@Component 2451export struct ForEachCom { 2452 arrayList: UnionData[] = [new Data(), new Data(), new Data()]; 2453 @BuilderParam closer: (data: UnionData) => void = this.componentCloser; 2454 2455 @Builder 2456 componentCloser() { 2457 } 2458 2459 build() { 2460 Column() { 2461 ForEach(this.arrayList, (item: UnionData) => { 2462 Row() { 2463 this.closer(item) 2464 }.width('100%').height(200).backgroundColor('#eee') 2465 }) 2466 } 2467 } 2468} 2469```