1# Adaptation Cases 2 3In this document, various use cases are presented to provide suggestions on adapting TS code to ArkTS for compliance with ArkTS syntax rules. Each chapter is named after an ArkTS syntax rule. Each use case provides the TS code before adaptation and the ArkTS code after adaptation. 4 5## arkts-identifiers-as-prop-names 6 7**Before adaptation** 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**After adaptation** 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### Changing any and unknown to Specific Types 42 43```typescript 44function printObj(obj: any) { 45 console.log(obj); 46} 47 48printObj('abc'); 49``` 50 51**After adaptation** 52 53```typescript 54function printObj(obj: string) { 55 console.log(obj); 56} 57 58printObj('abc'); 59``` 60 61### Marking JSON.parse Return Value Type 62 63**Before adaptation** 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**After adaptation** 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### Using Record Type 98 99**Before adaptation** 100 101```typescript 102function printProperties(obj: any) { 103 console.log(obj.name); 104 console.log(obj.value); 105} 106``` 107 108**After adaptation** 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 119Use the function type instead. 120 121**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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 215Use the **Record** type instead. 216 217**Before adaptation** 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**After adaptation** 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**Before adaptation** 240 241```typescript 242class C { 243 getInstance(): this { 244 return this; 245 } 246} 247``` 248 249**After adaptation** 250 251```typescript 252class C { 253 getInstance(): C { 254 return this; 255 } 256} 257``` 258 259## arkts-no-ctor-prop-decls 260 261**Before adaptation** 262 263```typescript 264class Person { 265 constructor(readonly name: string) {} 266 267 getName(): string { 268 return this.name; 269 } 270} 271``` 272 273**After adaptation** 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**Before adaptation** 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**After adaptation** 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 351Use the **Record** type to access object attributes. 352 353**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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**Reason for change** 402 403**(item instanceof C) ? item: null** needs to be declared as type **C | null**. This is because the compiler cannot deduce the generic type parameter of **map**, and explicit type annotation is required. 404 405## arkts-no-regexp-literals 406 407**Before adaptation** 408 409```typescript 410let regex: RegExp = /\s*/g; 411``` 412 413**After adaptation** 414 415```typescript 416let regexp: RegExp = new RegExp('\\s*','g'); 417``` 418 419**Reason for change** 420 421To include a flag in a regular expression, use it as a parameter of **new RegExp()**. 422 423## arkts-no-untyped-obj-literals 424 425### Specifying Object Literal Type for Type Imports 426 427**Before adaptation** 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**After adaptation** 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### Using a Class for Object Literal Type Annotation Only When the Class Constructor Have No Parameters 452 453**Before adaptation** 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**After adaptation: mode 1** 468 469```typescript 470// Remove the constructor. 471class Test { 472 value: number = 1 473} 474 475let t: Test = { value: 2 }; 476``` 477 478**After adaptation: mode 2** 479```typescript 480// Use 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**Reason for change** 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); // An exception is thrown. 507let t: C = { value: -2 }; // Not supported by ArkTS. 508``` 509 510In the preceding example, if **C** is allowed to be used to specify the object literal type, the variable **t** in the code will cause ambiguity of behavior. In light of this, ArkTS does not allow for object literal type annotation that may cause this issue. 511 512### Using an Identifier as the Object Literal Key When Specifying the Object Literal Type with a Class or Interface 513 514**Before adaptation** 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**After adaptation** 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### If **Record** is used to specify the object literal type, a string must be used as the key of the object literal. 554 555**Before adaptation** 556 557```typescript 558let obj: Record<string, number | string> = { 559 value: 123, 560 name: 'abc' 561} 562``` 563 564**After adaptation** 565 566```typescript 567let obj: Record<string, number | string> = { 568 'value': 123, 569 'name': 'abc' 570} 571``` 572 573### Including Index Signature in the Function Parameter Type 574 575**Before adaptation** 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**After adaptation** 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### Including Object Literals in Actual Parameters of Functions 598 599**Before adaptation** 600 601```typescript 602(fn) => { 603 fn({ value: 123, name:'' }); 604} 605``` 606 607**After adaptation** 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### Including Methods in Classes or Interfaces 621 622**Before adaptation** 623 624```typescript 625interface T { 626 foo(value: number): number 627} 628 629let t:T = { foo: (value) => { return value } }; 630``` 631 632**After adaptation: mode 1** 633 634```typescript 635interface T { 636 foo: (value: number) => number 637} 638 639let t:T = { foo: (value) => { return value } }; 640``` 641 642**After adaptation: mode 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**Reason for change** 655 656The methods declared in a class or interface should be shared by all instances of the class. In ArkTS, object literals cannot be used to rewrite instance methods. ArkTS supports attributes of the function type. 657 658### export default Object 659 660**Before adaptation** 661 662```typescript 663export default { 664 onCreate() { 665 // ... 666 }, 667 onDestroy() { 668 // ... 669 } 670} 671``` 672 673**After adaptation** 674 675```typescript 676class Test { 677 onCreate() { 678 // ... 679 } 680 onDestroy() { 681 // ... 682 } 683} 684 685export default new Test() 686``` 687 688### Obtaining the Type by Importing a Namespace 689 690**Before adaptation** 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**After adaptation** 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**Reason for change** 735 736The object literal lacks a type. According to the analysis of **test.foo**, the **option** type comes from the declaration file. Therefore, you only need to import the type. 737In **test.d.ets**, **I** is defined in the namespace. Therefore, to import the type in the .ets file, import the namespace and then obtain the target type based on the name. 738 739### Passing Parameters from the Object Literal to the Object Type 740 741**Before adaptation** 742 743```typescript 744function emit(event: string, ...args: Object[]): void {} 745 746emit('', { 747 'action': 11, 748 'outers': false 749}); 750``` 751 752**After adaptation** 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**Before adaptation** 768 769```typescript 770type Person = { name: string, age: number } 771``` 772 773**After adaptation** 774 775```typescript 776interface Person { 777 name: string, 778 age: number 779} 780``` 781 782## arkts-no-noninferrable-arr-literals 783 784**Before adaptation** 785 786```typescript 787let permissionList = [ 788 {name: 'Device information', value: 'Used to analyze the battery life, call, Internet access, and SIM card issues of the device.'}, 789 {name: 'Microphone', value: 'Used to add voice when reporting an issue.'}, 790 {"name: 'Storage', value: 'Used to add local file attachments when reporting an issue.'} 791] 792``` 793 794**After adaptation** 795 796Declare the type for the object literal. 797 798```typescript 799class PermissionItem { 800 name?: string 801 value?: string 802} 803 804let permissionList: PermissionItem[] = [ 805 {name: 'Device information', value: 'Used to analyze the battery life, call, Internet access, and SIM card issues of the device.'}, 806 {name: 'Microphone', value: 'Used to add voice when reporting an issue.'}, 807 {"name: 'Storage', value: 'Used to add local file attachments when reporting an issue.'} 808] 809``` 810 811## arkts-no-method-reassignment 812 813**Before adaptation** 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**After adaptation** 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**Before adaptation** 851 852```typescript 853let a = +'5'; 854let b = -'5'; 855let c = ~'5'; 856let d = +'string'; 857``` 858 859**After adaptation** 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**Before adaptation** 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**After adaptation** 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### Using Object.keys to Determine Whether an Attribute Exists 903 904**Before adaptation** 905 906```typescript 907function test(str: string, obj: Record<string, Object>) { 908 return str in obj; 909} 910``` 911 912**After adaptation** 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**Before adaptation** 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**After adaptation** 938 939Use arrays. 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**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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**Before adaptation** 1039 1040```typescript 1041import { BusinessError } from '@kit.BasicServicesKit' 1042 1043function ThrowError(error: BusinessError) { 1044 throw error; 1045} 1046``` 1047 1048**After adaptation** 1049 1050```typescript 1051import { BusinessError } from '@kit.BasicServicesKit' 1052 1053function ThrowError(error: BusinessError) { 1054 throw error as Error; 1055} 1056``` 1057 1058**Reason for change** 1059 1060The type of the value in the **throw** statement must be **Error** or its inheritance class. If the inheritance class is a generic, an error is reported during compilation. You are advised to use **as** to convert the type to **Error**. 1061 1062## arkts-no-standalone-this 1063 1064### Using this in a Function 1065 1066**Before adaptation** 1067 1068```typescript 1069function foo() { 1070 console.log(this.value); 1071} 1072 1073let obj = { value: 'abc' }; 1074foo.apply(obj); 1075``` 1076 1077**After adaptation: mode 1** 1078 1079Use the method of a class. If the method is used by multiple classes, consider using the inheritance mechanism. 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**After adaptation: mode 2** 1098 1099Passing this as a Parameter 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**After adaptation: mode 3** 1115 1116Pass the attribute as a parameter. 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### Using this in the Static Method of a Class 1131 1132**Before adaptation** 1133 1134```typescript 1135class Test { 1136 static value: number = 123 1137 static foo(): number { 1138 return this.value 1139 } 1140} 1141``` 1142 1143**After adaptation** 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**Before adaptation** 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**After adaptation** 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**Reason for change** 1203 1204In ArkTS, the object layout is determined at compile time. To assign all attributes of an object to another object, you can use the attribute-by-attribute assignment statement. In this example, the type of the source object s the same as that of the target object. In this case, you can reconstruct the code by changing the object attribute. 1205 1206## arkts-no-ctor-signatures-funcs 1207 1208Declare attributes within a class, not on a constructor. 1209 1210**Before adaptation** 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**After adaptation** 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 1264ArkTS does not support **globalThis** for two reasons:<br>(1) A static type cannot be added for **globalThis**. As a result, the attributes of **globalThis** can be accessed only through search, which causes extra performance overhead.<br>(2) Type annotation is not available for attributes of **globalThis**. As a result, the security and performance of operations on these attributes cannot be ensured. Therefore, ArkTS does not support **globalThis**. 1265 12661. You are advised to transfer data between modules based on the service logic and import/export syntax. 1267 12682. If necessary, you can construct a singleton object to implement the function of a global object. (Note: The singleton object cannot be defined in a HAR file, which packs two copies in different HAP files and therefore cannot implement singleton objects.) 1269 1270Construct a singleton object. 1271 1272```typescript 1273// Construct a singleton object. 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**Before adaptation** 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**After adaptation** 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### Using Interfaces in the Standard Library 1341 1342**Before adaptation** 1343 1344```typescript 1345let arr: number[] = [1, 2, 3, 4]; 1346let str = String.fromCharCode.apply(null, Array.from(arr)); 1347``` 1348 1349**After adaptation** 1350 1351```typescript 1352let arr: number[] = [1, 2, 3, 4]; 1353let str = String.fromCharCode(...Array.from(arr)); 1354``` 1355 1356### Using bind in Method Definitions 1357 1358**Before adaptation** 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**After adaptation: mode 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**After adaptation: mode 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### Using apply 1421 1422**Before adaptation** 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**After adaptation** 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**Before adaptation** 1473 1474```typescript 1475let entries = new Map([ 1476 ['foo', 123], 1477 ['bar', 456] 1478]); 1479 1480let obj = Object.fromEntries(entries); 1481``` 1482 1483**After adaptation** 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### Using Attributes and Methods of Number 1500 1501ArkTS does not allow the use of the following attributes and methods for global objects: **Infinity**, **NaN**, **isFinite**, **isNaN**, **parseFloat**, and **parseInt** 1502 1503You can use them for **Number**. 1504 1505**Before adaptation** 1506 1507```typescript 1508NaN; 1509isFinite(123); 1510parseInt('123'); 1511``` 1512 1513**After adaptation** 1514 1515```typescript 1516Number.NaN; 1517Number.isFinite(123); 1518Number.parseInt('123'); 1519``` 1520 1521## arkts-strict-typing(StrictModeError) 1522 1523### strictPropertyInitialization 1524 1525**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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### Strict Attribute Initialization Check 1609 1610In a class, if an attribute is not initialized and is not assigned a value in the constructor, ArkTS reports an error. 1611 1612**After adaptation** 1613 16141. Whenever possible, initialize attributes during declaration based on service logic or assign values to the attributes in constructors. Example: 1615 1616```typescript 1617//code with error 1618class Test { 1619 value: number 1620 flag: boolean 1621} 1622 1623// Method 1: Initialize attributes during declaration. 1624class Test { 1625 value: number = 0 1626 flag: boolean = false 1627} 1628 1629// Method 2: Assign values to attributes in the constructor. 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. For object type (including function type) **A**, if you are not sure how to initialize it, you are advised to initialize it in one of the following ways: 1641 1642 Mode (i): **prop: A | null = null** 1643 1644 Mode (ii): **prop?:A** 1645 1646 Mode 3 (iii): **prop: A | undefined = undefined** 1647 1648- From the perspective of performance, the **null** type is used only for type check during compilation and has no impact on VM performance. In contrast, **undefined | A** is treated as a union type and may result in additional overhead at run time. 1649- In terms of code readability and simplicity, **prop?:A** is the syntax sugar of **prop: A | undefined = undefined**. You are advised to use optional attributes. 1650 1651### Strict Function Type Check 1652 1653**Before adaptation** 1654 1655```typescript 1656function foo(fn: (value?: string) => void, value: string): void {} 1657 1658foo((value: string) => {}, ''); //error 1659``` 1660 1661**After adaptation** 1662 1663```typescript 1664function foo(fn: (value?: string) => void, value: string): void {} 1665 1666foo((value?: string) => {}, ''); 1667``` 1668 1669**Reason for change** 1670 1671In the following example, if strict function type check is not enabled during compilation, the code can be compiled successfully, but unexpected behavior occurs at run time. Specifically, in the function body of **foo**, an **undefined** is passed in to **fn** (this is acceptable because **fn** can accept **undefined**). However, at the invoking point of **foo** in line 6 of the code, in the passed function implementation of **(value: string) => { console.log(value.toUpperCase()) }**, the **value** parameter is always of the string type and can call the **toUpperCase** method. If strict function type check is not enabled, an error indicating that the property cannot be found on **undefined** occurs at run time. 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 1682If strict type check is enabled during compilation, the preceding issue can be detected at compile time. 1683 1684### Strict Null Check 1685 1686**Before adaptation** 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**After adaptation** 1702 1703When writing code, minimize the use of nullable types. If a variable or property is marked with a nullable type, a null check is required. Process the service logic based on whether the variable or property is null. 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**Reason for change** 1721 1722In the first code segment, if strict null check is not enabled during compilation, the code segment can be compiled successfully, but unexpected behavior occurs at run time. This is because the **value** property of **t** is **undefined** (**value?: string** is the syntax sugar of **value: string | undefined = undefined**), and when the **printValue** method is called in line 11, the property is directly accessed based on the string type, due to a lack of null check on the value of **this.value** in the method body. To avoid unexpected behavior at run time, enable strict null check during compilation. 1723 1724### Function Return Type Mismatch 1725 1726**Before adaptation** 1727 1728```typescript 1729class Test { 1730 handleClick: (action: string, externInfo?: string) => void | null = null; 1731} 1732``` 1733 1734**After adaptation** 1735 1736In the original code, the return type of the function is parsed as **void | undefined**. Add parentheses to distinguish the union type. 1737 1738```typescript 1739class Test { 1740 handleClick: ((action: string, externInfo?: string) => void) | null = null; 1741} 1742``` 1743 1744### '***' is of type 'unknown' 1745 1746**Before adaptation** 1747 1748```typescript 1749try { 1750 1751} catch (error) { 1752 console.log(error.message); 1753} 1754``` 1755 1756**After adaptation** 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**Before adaptation** 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**After adaptation: mode 1** 1791 1792Change the type of variable **a** to **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 // Non-empty branch 1813} else { 1814 // Process null. 1815} 1816``` 1817 1818**After adaptation: mode 2** 1819 1820If you can determine that a non-null value is returned when **foo** is called, you can use a non-null assertion operator **!**. 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**Before adaptation** 1843 1844```typescript 1845interface A { 1846 foo?: () => void 1847} 1848 1849let a:A = { foo: () => {} }; 1850a.foo(); 1851``` 1852 1853**After adaptation: mode 1** 1854 1855```typescript 1856interface A { 1857 foo: () => void 1858} 1859let a: A = { foo: () => {} }; 1860a.foo(); 1861``` 1862 1863**After adaptation: mode 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**Reason for change** 1877 1878In the original code definition, **foo** is an optional property and may be **undefined**. If **undefined** is called, an error is reported. You are advised to determine whether a property is optional based on the service logic. If defining an optional property is necessary, a null check is required for accessing the property. 1879 1880### Variable '***' is used before being assigned 1881 1882**Before adaptation** 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**After adaptation** 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**Reason for change** 1920 1921For primitive types, a value can be assigned based on the service logic, for example, **0**, **''**, and **false**. 1922 1923For the object type, you can change the type to a union type consisting of **null** and assign **null** to the type. In this case, when using the object type, you need to perform the non-null check. 1924 1925### Function lacks ending return statement and return type does not include 'undefined'. 1926 1927**Before adaptation** 1928 1929```typescript 1930function foo(a: number): number { 1931 if (a > 0) { 1932 return a; 1933 } 1934} 1935``` 1936 1937**After adaptation: mode 1** 1938 1939Return a proper value in the **else** branch based on the service logic. 1940 1941**After adaptation: mode 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**Before adaptation** 1955 1956```typescript 1957// @ts-nocheck 1958var a: any = 123; 1959``` 1960 1961**After adaptation** 1962 1963```typescript 1964let a: number = 123; 1965``` 1966 1967**Reason for change** 1968 1969ArkTS does not support the use of comments to bypass strict type checks. Delete the comment (**// @ts-nocheck** or **// @ts-ignore**), and then modify other code based on the error information. 1970 1971## Importing ArkTS files to JS and TS files is not allowed 1972 1973## arkts-no-tsdeps 1974 1975In .ts and .js files, it is not allowed to import source code from an .ets file. 1976 1977**After adaptation** 1978 1979Mode 1: Change the file name extension of the .ts file to .ets and adapt the code based on the ArkTS syntax rules. 1980 1981Mode 2: Extract the code that the .ts file depends on from the .ets file to the .ts file. 1982 1983## arkts-no-special-imports 1984 1985**Before adaptation** 1986 1987```typescript 1988import type {A, B, C, D } from '***' 1989``` 1990 1991 1992**After adaptation** 1993 1994```typescript 1995import {A, B, C, D } from '***' 1996``` 1997 1998## arkts-no-classes-as-obj 1999 2000### Using Class to Construct an Instance 2001 2002**Before adaptation** 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**After adaptation** 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### Accessing Static Properties 2057 2058**Before adaptation** 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**After adaptation** 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 2102Use Dynamic Imports 2103 2104**Before adaptation** 2105 2106```typescript 2107import 'module' 2108``` 2109 2110**After adaptation** 2111 2112```typescript 2113import('module') 2114``` 2115 2116## arkts-no-func-props 2117 2118**Before adaptation** 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**After adaptation** 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**Before adaptation** 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**After adaptation** 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## Copy 2190 2191### Shallow Copy 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### Deep Copy 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## Typical Application Scenarios of State Management 2250 2251### Using State Variables Outside of Structs 2252 2253The struct is different from the class. Therefore, avoid passing **this** as a parameter to the outside of the struct. Otherwise, the instance reference cannot be released and memory leakage may occur. You are advised to pass the state variable object outside the struct and modify the object properties to trigger UI re-render. 2254 2255**Not recommended** 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); // You are not advised to pass this as a parameter to the outside 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**Recommended** 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### Using Union Types in Structs 2362 2363The following code contains the arkts-no-any-unknown error. Because the struct does not support generics, you are advised to use the union type to implement generic-like functions of custom components. 2364 2365**Not recommended** 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[]; // The struct does not support generics. An arkts-no-any-unknown error is reported. 2396 @BuilderParam closer: (data: any) => void = this.componentCloser; // The struct does not support generics. An arkts-no-any-unknown error is reported. 2397 2398 @Builder 2399 componentCloser() { 2400 } 2401 2402 build() { 2403 Column() { 2404 ForEach(this.arrayList, (item: any) => { // The struct does not support generics. An arkts-no-any-unknown error is reported. 2405 Row() { 2406 this.closer(item) 2407 }.width('100%').height(200).backgroundColor('#eee') 2408 }) 2409 } 2410 } 2411} 2412``` 2413 2414**Recommended** 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``` 2470