1# Adaptation Cases 2 3<!--Kit: ArkTS--> 4<!--Subsystem: ArkCompiler--> 5<!--Owner: @anxuesm--> 6<!--Designer: @qyhuo32--> 7<!--Tester: @kirl75; @zsw_zhushiwei--> 8<!--Adviser: @zhang_yixin13--> 9 10In this topic, specific 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. 11 12## arkts-identifiers-as-prop-names 13 14If the property name is a valid identifier (that is, special characters and spaces are excluded, and does not start with a digit), it can be used directly without quotes. 15 16**Before adaptation** 17 18```typescript 19interface W { 20 bundleName: string 21 action: string 22 entities: string[] 23} 24 25let wantInfo: W = { 26 'bundleName': 'com.huawei.hmos.browser', 27 'action': 'ohos.want.action.viewData', 28 'entities': ['entity.system.browsable'] 29} 30``` 31 32**After adaptation** 33 34```typescript 35interface W { 36 bundleName: string 37 action: string 38 entities: string[] 39} 40 41let wantInfo: W = { 42 bundleName: 'com.huawei.hmos.browser', 43 action: 'ohos.want.action.viewData', 44 entities: ['entity.system.browsable'] 45} 46``` 47 48## arkts-no-any-unknown 49 50### Changing any and unknown to Specific Types 51 52```typescript 53function printObj(obj: any) { 54 console.log(obj); 55} 56 57printObj('abc'); // abc 58``` 59 60**After adaptation** 61 62```typescript 63function printObj(obj: string) { 64 console.log(obj); 65} 66 67printObj('abc'); // abc 68``` 69 70### Marking JSON.parse Return Value Type 71 72**Before adaptation** 73 74```typescript 75class A { 76 v: number = 0 77 s: string = '' 78 79 foo(str: string) { 80 let tmpStr = JSON.parse(str); 81 if (tmpStr.add != undefined) { 82 this.v = tmpStr.v; 83 this.s = tmpStr.s; 84 } 85 } 86} 87``` 88 89**After adaptation** 90 91```typescript 92class A { 93 v: number = 0 94 s: string = '' 95 96 foo(str: string) { 97 let tmpStr: Record<string, Object> = JSON.parse(str); 98 if (tmpStr.add != undefined) { 99 this.v = tmpStr.v as number; 100 this.s = tmpStr.s as string; 101 } 102 } 103} 104``` 105 106### Using Record Type 107 108**Before adaptation** 109 110```typescript 111function printProperties(obj: any) { 112 console.log(obj.name); 113 console.log(obj.value); 114} 115``` 116 117**After adaptation** 118 119```typescript 120function printProperties(obj: Record<string, Object>) { 121 console.log(obj.name as string); 122 console.log(obj.value as string); 123} 124``` 125 126## arkts-no-call-signature 127 128Use the function type instead. 129 130**Before adaptation** 131 132```typescript 133interface I { 134 (value: string): void; 135} 136 137function foo(fn: I) { 138 fn('abc'); 139} 140 141foo((value: string) => { 142 console.log(value); 143}) 144``` 145 146 147**After adaptation** 148 149```typescript 150type I = (value: string) => void 151 152function foo(fn: I) { 153 fn('abc'); 154} 155 156foo((value: string) => { 157 console.log(value); 158}) 159``` 160 161## arkts-no-ctor-signatures-type 162 163Use the factory function (**() => Instance**) instead of the constructor signature. 164 165**Before adaptation** 166 167```typescript 168class Controller { 169 value: string = '' 170 171 constructor(value: string) { 172 this.value = value; 173 } 174} 175 176type ControllerConstructor = { 177 new (value: string): Controller; 178} 179 180class Menu { 181 controller: ControllerConstructor = Controller 182 createController() { 183 if (this.controller) { 184 return new this.controller('123'); 185 } 186 return null; 187 } 188} 189 190let t = new Menu(); 191console.log(t.createController()!.value); 192``` 193 194**After adaptation** 195 196```typescript 197class Controller { 198 value: string = '' 199 200 constructor(value: string) { 201 this.value = value; 202 } 203} 204 205type ControllerConstructor = () => Controller; 206 207class Menu { 208 controller: ControllerConstructor = () => { 209 return new Controller('abc'); 210 } 211 212 createController() { 213 if (this.controller) { 214 return this.controller(); 215 } 216 return null; 217 } 218} 219 220let t: Menu = new Menu(); 221console.log(t.createController()!.value); 222``` 223 224## arkts-no-indexed-signatures 225 226Use the **Record** type instead. 227 228**Before adaptation** 229 230```typescript 231function foo(data: { [key: string]: string }) { 232 data['a'] = 'a'; 233 data['b'] = 'b'; 234 data['c'] = 'c'; 235} 236``` 237 238**After adaptation** 239 240```typescript 241function foo(data: Record<string, string>) { 242 data['a'] = 'a'; 243 data['b'] = 'b'; 244 data['c'] = 'c'; 245} 246``` 247 248## arkts-no-typing-with-this 249 250Use a specific type instead of **this**. 251 252**Before adaptation** 253 254```typescript 255class C { 256 getInstance(): this { 257 return this; 258 } 259} 260``` 261 262**After adaptation** 263 264```typescript 265class C { 266 getInstance(): C { 267 return this; 268 } 269} 270``` 271 272## arkts-no-ctor-prop-decls 273 274Declare class properties explicitly and manually assign values to the constructor. 275 276**Before adaptation** 277 278```typescript 279class Person { 280 constructor(readonly name: string) {} 281 282 getName(): string { 283 return this.name; 284 } 285} 286``` 287 288**After adaptation** 289 290```typescript 291class Person { 292 name: string 293 constructor(name: string) { 294 this.name = name; 295 } 296 297 getName(): string { 298 return this.name; 299 } 300} 301``` 302 303## arkts-no-ctor-signatures-iface 304 305Use type to define factory functions or common function types. 306 307**Before adaptation** 308 309```typescript 310class Controller { 311 value: string = '' 312 313 constructor(value: string) { 314 this.value = value; 315 } 316} 317 318interface ControllerConstructor { 319 new (value: string): Controller; 320} 321 322class Menu { 323 controller: ControllerConstructor = Controller 324 createController() { 325 if (this.controller) { 326 return new this.controller('abc'); 327 } 328 return null; 329 } 330} 331 332let t = new Menu(); 333console.log(t.createController()!.value); 334``` 335 336**After adaptation** 337 338```typescript 339class Controller { 340 value: string = '' 341 342 constructor(value: string) { 343 this.value = value; 344 } 345} 346 347type ControllerConstructor = () => Controller; 348 349class Menu { 350 controller: ControllerConstructor = () => { 351 return new Controller('abc'); 352 } 353 354 createController() { 355 if (this.controller) { 356 return this.controller(); 357 } 358 return null; 359 } 360} 361 362let t: Menu = new Menu(); 363console.log(t.createController()!.value); 364``` 365 366## arkts-no-props-by-index 367 368Use the **Record** type to access object properties. 369 370**Before adaptation** 371 372```typescript 373function foo(params: Object) { 374 let funNum: number = params['funNum']; 375 let target: string = params['target']; 376} 377``` 378 379**After adaptation** 380 381```typescript 382function foo(params: Record<string, string | number>) { 383 let funNum: number = params['funNum'] as number; 384 let target: string = params['target'] as string; 385} 386``` 387 388## arkts-no-inferred-generic-params 389 390Explicitly specify the generic parameter types, such as **Map\<string, T\>** and **.map\<T\>()**, for all generic invocations. 391 392**Before adaptation** 393 394```typescript 395class A { 396 str: string = '' 397} 398class B extends A {} 399class C extends A {} 400 401let arr: Array<A> = []; 402 403let originMenusMap:Map<string, C> = new Map(arr.map(item => [item.str, (item instanceof C) ? item: null])); 404``` 405 406**After adaptation** 407 408```typescript 409class A { 410 str: string = '' 411} 412class B extends A {} 413class C extends A {} 414 415let arr: Array<A> = []; 416 417let originMenusMap: Map<string, C | null> = new Map<string, C | null>(arr.map<[string, C | null]>(item => [item.str, (item instanceof C) ? item: null])); 418``` 419 420**Reason for change** 421 422**(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. 423 424## arkts-no-regexp-literals 425 426Use **new RegExp(pattern, flags)** instead of the **RegExp** literal. 427 428**Before adaptation** 429 430```typescript 431let regex: RegExp = /\s*/g; 432``` 433 434**After adaptation** 435 436```typescript 437let regexp: RegExp = new RegExp('\\s*','g'); 438``` 439 440**Reason for change** 441 442To include a flag in a regular expression, use it as a parameter of **new RegExp()**. 443 444## arkts-no-untyped-obj-literals 445 446### Specifying Object Literal Type for Type Imports 447 448**Before adaptation** 449 450```typescript 451const area = { // No type is specified, so it is inconvenient for maintenance. 452 pixels: new ArrayBuffer(8), 453 offset: 0, 454 stride: 8, 455 region: { size: { height: 1,width:2 }, x: 0, y: 0 } 456} 457``` 458 459**After adaptation** 460 461```typescript 462import { image } from '@kit.ImageKit'; 463 464const area: image.PositionArea = { // Type is specified. 465 pixels: new ArrayBuffer(8), 466 offset: 0, 467 stride: 8, 468 region: { size: { height: 1, width: 2 }, x: 0, y: 0 } 469} 470``` 471 472### Using a Class for Object Literal Type Annotation Only When the Class Constructor Has No Parameters 473 474**Before adaptation** 475 476```typescript 477class Test { 478 value: number = 1 479 // There is a constructor. 480 constructor(value: number) { 481 this.value = value; 482 } 483} 484 485let t: Test = { value: 2 }; 486``` 487 488**After adaptation: mode 1** 489 490```typescript 491// Remove the constructor. 492class Test { 493 value: number = 1 494} 495 496let t: Test = { value: 2 }; 497``` 498 499**After adaptation: mode 2** 500```typescript 501// Use new. 502class Test { 503 value: number = 1 504 505 constructor(value: number) { 506 this.value = value; 507 } 508} 509 510let t: Test = new Test(2); 511``` 512 513**Reason for change** 514 515```typescript 516class C { 517 value: number = 1 518 519 constructor(n: number) { 520 if (n < 0) { 521 throw new Error('Negative'); 522 } 523 this.value = n; 524 } 525} 526 527let s: C = new C(-2); // An exception is thrown. 528let t: C = { value: -2 }; // Not supported by ArkTS. 529``` 530 531In the preceding example, if **C** can 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. 532 533### Using an Identifier as the Object Literal Key When Specifying the Object Literal Type with a Class or Interface 534 535**Before adaptation** 536 537```typescript 538class Test { 539 value: number = 0 540} 541 542let arr: Test[] = [ 543 { 544 'value': 1 545 }, 546 { 547 'value': 2 548 }, 549 { 550 'value': 3 551 } 552] 553``` 554 555**After adaptation** 556 557```typescript 558class Test { 559 value: number = 0 560} 561let arr: Test[] = [ 562 { 563 value: 1 564 }, 565 { 566 value: 2 567 }, 568 { 569 value: 3 570 } 571] 572``` 573 574### Using a String as the Object Literal Key When Specifying the Object Literal Type with a Record 575 576**Before adaptation** 577 578```typescript 579let obj: Record<string, number | string> = { 580 value: 123, 581 name: 'abc' 582} 583``` 584 585**After adaptation** 586 587```typescript 588let obj: Record<string, number | string> = { 589 'value': 123, 590 'name': 'abc' 591} 592``` 593 594### Including Index Signature in the Function Parameter Type 595 596**Before adaptation** 597 598```typescript 599function foo(obj: { [key: string]: string}): string { 600 if (obj != undefined && obj != null) { 601 return obj.value1 + obj.value2; 602 } 603 return ''; 604} 605``` 606 607**After adaptation** 608 609```typescript 610function foo(obj: Record<string, string>): string { 611 if (obj != undefined && obj != null) { 612 return obj.value1 + obj.value2; 613 } 614 return ''; 615} 616``` 617 618### Including Object Literals in Actual Parameters of Functions 619 620**Before adaptation** 621 622```typescript 623(fn) => { 624 fn({ value: 123, name:'' }); 625} 626``` 627 628**After adaptation** 629 630```typescript 631class T { 632 value: number = 0 633 name: string = '' 634} 635 636(fn: (v: T) => void) => { 637 fn({ value: 123, name: '' }); 638} 639``` 640 641### Including Methods in Classes or Interfaces 642 643**Before adaptation** 644 645```typescript 646interface T { 647 foo(value: number): number 648} 649 650let t:T = { foo: (value) => { return value } }; 651``` 652 653**After adaptation: mode 1** 654 655```typescript 656interface T { 657 foo: (value: number) => number 658} 659 660let t:T = { foo: (value) => { return value } }; 661``` 662 663**After adaptation: mode 2** 664 665```typescript 666class T { 667 foo: (value: number) => number = (value: number) => { 668 return value; 669 } 670} 671 672let t:T = new T(); 673``` 674 675**Reason for change** 676 677The 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 properties of the function type. 678 679### export default Object 680 681**Before adaptation** 682 683```typescript 684export default { 685 onCreate() { 686 // ... 687 }, 688 onDestroy() { 689 // ... 690 } 691} 692``` 693 694**After adaptation** 695 696```typescript 697class Test { 698 onCreate() { 699 // ... 700 } 701 onDestroy() { 702 // ... 703 } 704} 705 706export default new Test() 707``` 708 709### Obtaining the Type by Importing a Namespace 710 711**Before adaptation** 712 713```typescript 714// test.d.ets 715declare namespace test { 716 interface I { 717 id: string; 718 type: number; 719 } 720 721 function foo(name: string, option: I): void; 722} 723 724export default test; 725 726// app.ets 727import test from 'test'; 728 729let option = { id: '', type: 0 }; 730test.foo('', option); 731``` 732 733**After adaptation** 734 735```typescript 736// test.d.ets 737declare namespace test { 738 interface I { 739 id: string; 740 type: number; 741 } 742 743 function foo(name: string, option: I): void; 744} 745 746export default test; 747 748// app.ets 749import test from 'test'; 750 751let option: test.I = { id: '', type: 0 }; 752test.foo('', option); 753``` 754 755**Reason for change** 756 757The 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. 758In **test.d.ets**, **I** is defined in **namespace**. Import **namespace** to the .ets file and then obtain t he corresponding type by name. 759 760### Passing Parameters from the Object Literal to the Object Type 761 762**Before adaptation** 763 764```typescript 765function emit(event: string, ...args: Object[]): void {} 766 767emit('', { 768 'action': 11, 769 'outers': false 770}); 771``` 772 773**After adaptation** 774 775```typescript 776function emit(event: string, ...args: Object[]): void {} 777 778let emitArg: Record<string, number | boolean> = { 779 'action': 11, 780 'outers': false 781} 782 783emit('', emitArg); 784``` 785 786## arkts-no-obj-literals-as-types 787 788Use **interface** to explicitly define the structure type. 789 790**Before adaptation** 791 792```typescript 793type Person = { name: string, age: number } 794``` 795 796**After adaptation** 797 798```typescript 799interface Person { 800 name: string, 801 age: number 802} 803``` 804 805## arkts-no-noninferrable-arr-literals 806 807Explicitly declare the type of array elements (using **interface** or **class**) and add type annotations to array variables. 808 809**Before adaptation** 810 811```typescript 812let permissionList = [ 813 {name: 'Device information', value: 'Used to analyze the battery life, call, Internet access, and SIM card issues of the device.'}, 814 {name: 'Microphone', value: 'Used to add voice when reporting an issue.'}, 815 {"name: 'Storage', value: 'Used to add local file attachments when reporting an issue.'} 816] 817``` 818 819**After adaptation** 820 821Declare the type for the object literal. 822 823```typescript 824class PermissionItem { 825 name?: string 826 value?: string 827} 828 829let permissionList: PermissionItem[] = [ 830 {name: 'Device information', value: 'Used to analyze the battery life, call, Internet access, and SIM card issues of the device.'}, 831 {name: 'Microphone', value: 'Used to add voice when reporting an issue.'}, 832 {"name: 'Storage', value: 'Used to add local file attachments when reporting an issue.'} 833] 834``` 835 836## arkts-no-method-reassignment 837 838Use class fields of the function type instead of prototype methods. 839 840**Before adaptation** 841 842```typescript 843class C { 844 add(left: number, right: number): number { 845 return left + right; 846 } 847} 848 849function sub(left: number, right: number): number { 850 return left - right; 851} 852 853let c1 = new C(); 854c1.add = sub; 855``` 856 857**After adaptation** 858 859```typescript 860class C { 861 add: (left: number, right: number) => number = 862 (left: number, right: number) => { 863 return left + right; 864 } 865} 866 867function sub(left: number, right: number): number { 868 return left - right; 869} 870 871let c1 = new C(); 872c1.add = sub; 873``` 874 875## arkts-no-polymorphic-unops 876 877Use explicit conversion functions such as **Number.parseInt()** and **new Number()**. 878 879**Before adaptation** 880 881```typescript 882let a = +'5'; // Implicit conversion using operators. 883let b = -'5'; 884let c = ~'5'; 885let d = +'string'; 886``` 887 888**After adaptation** 889 890```typescript 891let a = Number.parseInt('5'); // Explicit conversion using Number.parseInt. 892let b = -Number.parseInt('5'); 893let c = ~Number.parseInt('5'); 894let d = new Number('string'); 895``` 896 897## arkts-no-type-query 898 899Use **class**, **interface**, or type aliases instead of **typeof** to avoid inferring types based on variables. 900 901**Before adaptation** 902 903```typescript 904// module1.ts 905class C { 906 value: number = 0 907} 908 909export let c = new C() 910 911// module2.ts 912import { c } from './module1' 913let t: typeof c = { value: 123 }; 914``` 915 916**After adaptation** 917 918```typescript 919// module1.ts 920class C { 921 value: number = 0 922} 923 924export { C } 925 926// module2.ts 927import { C } from './module1' 928let t: C = { value: 123 }; 929``` 930 931## arkts-no-in 932 933### Using Object.keys to Determine Whether a Property Exists 934 935**Before adaptation** 936 937```typescript 938function test(str: string, obj: Record<string, Object>) { 939 return str in obj; 940} 941``` 942 943**After adaptation** 944 945```typescript 946function test(str: string, obj: Record<string, Object>) { 947 for (let i of Object.keys(obj)) { 948 if (i == str) { 949 return true; 950 } 951 } 952 return false; 953} 954``` 955 956## arkts-no-destruct-assignment 957 958Use index access or manual assignment instead of destructuring assignment. 959 960**Before adaptation** 961 962```typescript 963let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 964for (let [key, value] of map) { 965 console.log(key); 966 console.log(value); 967} 968``` 969 970**After adaptation** 971 972Use arrays. 973 974```typescript 975let map = new Map<string, string>([['a', 'a'], ['b', 'b']]); 976for (let arr of map) { 977 let key = arr[0]; 978 let value = arr[1]; 979 console.log(key); 980 console.log(value); 981} 982``` 983 984## arkts-no-types-in-catch 985 986Use the untyped **catch (error)** and then handle the error using type assertion. 987 988**Before adaptation** 989 990```typescript 991import { BusinessError } from '@kit.BasicServicesKit' 992 993try { 994 // ... 995} catch (e: BusinessError) { 996 console.error(e.message, e.code); 997} 998``` 999 1000**After adaptation** 1001 1002```typescript 1003import { BusinessError } from '@kit.BasicServicesKit' 1004 1005try { 1006 // ... 1007} catch (error) { 1008 let e: BusinessError = error as BusinessError; 1009 console.error(e.message, e.code); 1010} 1011``` 1012 1013## arkts-no-for-in 1014 1015Use **Object.entries(obj)** with **for...of** instead of **for...in**. 1016 1017**Before adaptation** 1018 1019```typescript 1020interface Person { 1021 [name: string]: string 1022} 1023let p: Person = { 1024 name: 'tom', 1025 age: '18' 1026}; 1027 1028for (let t in p) { 1029 console.log(p[t]); // log: "tom", "18" 1030} 1031``` 1032 1033**After adaptation** 1034 1035```typescript 1036let p: Record<string, string> = { 1037 'name': 'tom', 1038 'age': '18' 1039}; 1040 1041for (let ele of Object.entries(p)) { 1042 console.log(ele[1]); // log: "tom", "18" 1043} 1044``` 1045 1046## arkts-no-mapped-types 1047 1048Use **Record\<K, T\>** instead of the mapped type. 1049 1050**Before adaptation** 1051 1052```typescript 1053class C { 1054 a: number = 0 1055 b: number = 0 1056 c: number = 0 1057} 1058type OptionsFlags = { 1059 [Property in keyof C]: string 1060} 1061``` 1062 1063**After adaptation** 1064 1065```typescript 1066class C { 1067 a: number = 0 1068 b: number = 0 1069 c: number = 0 1070} 1071 1072type OptionsFlags = Record<keyof C, string> 1073``` 1074 1075## arkts-limited-throw 1076 1077Convert an object to **Error**, or create an **Error** instance and throw it. 1078 1079**Before adaptation** 1080 1081```typescript 1082import { BusinessError } from '@kit.BasicServicesKit' 1083 1084function ThrowError(error: BusinessError) { 1085 throw error; 1086} 1087``` 1088 1089**After adaptation** 1090 1091```typescript 1092import { BusinessError } from '@kit.BasicServicesKit' 1093 1094function ThrowError(error: BusinessError) { 1095 throw error as Error; 1096} 1097``` 1098 1099**Reason for change** 1100 1101The 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**. 1102 1103## arkts-no-standalone-this 1104 1105### Using this in a Function 1106 1107**Before adaptation** 1108 1109```typescript 1110function foo() { 1111 console.log(this.value); 1112} 1113 1114let obj = { value: 'abc' }; 1115foo.apply(obj); 1116``` 1117 1118**After adaptation: mode 1** 1119 1120Use the method of a class. If the method is used by multiple classes, consider using the inheritance mechanism. 1121 1122```typescript 1123class Test { 1124 value: string = '' 1125 constructor (value: string) { 1126 this.value = value 1127 } 1128 1129 foo() { 1130 console.log(this.value); 1131 } 1132} 1133 1134let obj: Test = new Test('abc'); 1135obj.foo(); 1136``` 1137 1138**After adaptation: mode 2** 1139 1140Pass in **this** as a parameter. 1141 1142```typescript 1143function foo(obj: Test) { 1144 console.log(obj.value); 1145} 1146 1147class Test { 1148 value: string = '' 1149} 1150 1151let obj: Test = { value: 'abc' }; 1152foo(obj); 1153``` 1154 1155**After adaptation: mode 3** 1156 1157Pass in a property as a parameter. 1158```typescript 1159function foo(value: string) { 1160 console.log(value); 1161} 1162 1163class Test { 1164 value: string = '' 1165} 1166 1167let obj: Test = { value: 'abc' }; 1168foo(obj.value); 1169``` 1170 1171### Using this in the Static Method of a Class 1172 1173**Before adaptation** 1174 1175```typescript 1176class Test { 1177 static value: number = 123 1178 static foo(): number { 1179 return this.value 1180 } 1181} 1182``` 1183 1184**After adaptation** 1185 1186```typescript 1187class Test { 1188 static value: number = 123 1189 static foo(): number { 1190 return Test.value 1191 } 1192} 1193``` 1194 1195## arkts-no-spread 1196 1197Use **Object.assign()**, manual assignment, or array methods instead of the spread operator. 1198 1199**Before adaptation** 1200 1201```typescript 1202// test.d.ets 1203declare namespace test { 1204 interface I { 1205 id: string; 1206 type: number; 1207 } 1208 1209 function foo(): I; 1210} 1211 1212export default test 1213 1214// app.ets 1215import test from 'test'; 1216 1217let t: test.I = { 1218 ...test.foo(), 1219 type: 0 1220} 1221``` 1222 1223**After adaptation** 1224 1225```typescript 1226// test.d.ets 1227declare namespace test { 1228 interface I { 1229 id: string; 1230 type: number; 1231 } 1232 1233 function foo(): I; 1234} 1235 1236export default test 1237 1238// app.ets 1239import test from 'test'; 1240 1241let t: test.I = test.foo(); 1242t.type = 0; 1243``` 1244 1245**Reason for change** 1246 1247In ArkTS, the object layout is determined at compile time. To assign all properties of an object to another object, you can use the property-by-property assignment statement. In this example, the type of the source object is the same as that of the target object. In this case, you can reconstruct the code by changing the object property. 1248 1249## arkts-no-ctor-signatures-funcs 1250 1251Declare properties within a class, not on a constructor. 1252 1253**Before adaptation** 1254 1255```typescript 1256class Controller { 1257 value: string = '' 1258 constructor(value: string) { 1259 this.value = value 1260 } 1261} 1262 1263type ControllerConstructor = new (value: string) => Controller; 1264 1265class Menu { 1266 controller: ControllerConstructor = Controller 1267 createController() { 1268 if (this.controller) { 1269 return new this.controller('abc'); 1270 } 1271 return null; 1272 } 1273} 1274 1275let t = new Menu() 1276console.log(t.createController()!.value) 1277``` 1278 1279**After adaptation** 1280 1281```typescript 1282class Controller { 1283 value: string = '' 1284 constructor(value: string) { 1285 this.value = value; 1286 } 1287} 1288 1289type ControllerConstructor = () => Controller; 1290 1291class Menu { 1292 controller: ControllerConstructor = () => { return new Controller('abc') } 1293 createController() { 1294 if (this.controller) { 1295 return this.controller(); 1296 } 1297 return null; 1298 } 1299} 1300 1301let t: Menu = new Menu(); 1302console.log(t.createController()!.value); 1303``` 1304 1305## arkts-no-globalthis 1306 1307ArkTS does not support **globalThis** for two reasons:<br> (1) A static type cannot be added for **globalThis**. As a result, the properties of **globalThis** can be accessed only through search, which causes extra performance overhead.<br> (2) Type annotation is not available for properties of **globalThis**. As a result, the security and performance of operations on these properties cannot be ensured. 1308 13091. You are advised to transfer data between modules based on the service logic and import/export syntax. 1310 13112. 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 packages two copies in different HAP files and therefore cannot implement singleton objects.) 1312 1313Construct a singleton object. 1314 1315```typescript 1316// Construct a singleton object. 1317export class GlobalContext { 1318 private constructor() {} 1319 private static instance: GlobalContext; 1320 private _objects = new Map<string, Object>(); 1321 1322 public static getContext(): GlobalContext { 1323 if (!GlobalContext.instance) { 1324 GlobalContext.instance = new GlobalContext(); 1325 } 1326 return GlobalContext.instance; 1327 } 1328 1329 getObject(value: string): Object | undefined { 1330 return this._objects.get(value); 1331 } 1332 1333 setObject(key: string, objectClass: Object): void { 1334 this._objects.set(key, objectClass); 1335 } 1336} 1337 1338``` 1339 1340**Before adaptation** 1341 1342```typescript 1343 1344// file1.ts 1345 1346export class Test { 1347 value: string = ''; 1348 foo(): void { 1349 globalThis.value = this.value; 1350 } 1351} 1352 1353// file2.ts 1354 1355globalThis.value; 1356 1357``` 1358 1359**After adaptation** 1360 1361```typescript 1362 1363// file1.ts 1364 1365import { GlobalContext } from '../GlobalContext' 1366 1367export class Test { 1368 value: string = ''; 1369 foo(): void { 1370 GlobalContext.getContext().setObject('value', this.value); 1371 } 1372} 1373 1374// file2.ts 1375 1376import { GlobalContext } from '../GlobalContext' 1377 1378GlobalContext.getContext().getObject('value'); 1379``` 1380 1381## arkts-no-func-apply-bind-call 1382 1383### Using Interfaces in the Standard Library 1384 1385**Before adaptation** 1386 1387```typescript 1388let arr: number[] = [1, 2, 3, 4]; 1389let str = String.fromCharCode.apply(null, Array.from(arr)); 1390``` 1391 1392**After adaptation** 1393 1394```typescript 1395let arr: number[] = [1, 2, 3, 4]; 1396let str = String.fromCharCode(...Array.from(arr)); 1397``` 1398 1399### Using bind in Method Definitions 1400 1401**Before adaptation** 1402 1403```typescript 1404class A { 1405 value: string = '' 1406 foo: Function = () => {} 1407} 1408 1409class Test { 1410 value: string = '1234' 1411 obj: A = { 1412 value: this.value, 1413 foo: this.foo.bind(this) 1414 } 1415 1416 foo() { 1417 console.log(this.value); 1418 } 1419} 1420``` 1421 1422**After adaptation: mode 1** 1423 1424```typescript 1425class A { 1426 value: string = '' 1427 foo: Function = () => {} 1428} 1429 1430class Test { 1431 value: string = '1234' 1432 obj: A = { 1433 value: this.value, 1434 foo: (): void => this.foo() 1435 } 1436 1437 foo() { 1438 console.log(this.value); 1439 } 1440} 1441``` 1442 1443**After adaptation: mode 2** 1444 1445```typescript 1446class A { 1447 value: string = '' 1448 foo: Function = () => {} 1449} 1450 1451class Test { 1452 value: string = '1234' 1453 foo: () => void = () => { 1454 console.log(this.value); 1455 } 1456 obj: A = { 1457 value: this.value, 1458 foo: this.foo 1459 } 1460} 1461``` 1462 1463### Using apply 1464 1465**Before adaptation** 1466 1467```typescript 1468class A { 1469 value: string; 1470 constructor (value: string) { 1471 this.value = value; 1472 } 1473 1474 foo() { 1475 console.log(this.value); 1476 } 1477} 1478 1479let a1 = new A('1'); 1480let a2 = new A('2'); 1481 1482a1.foo(); 1483a1.foo.apply(a2); 1484``` 1485 1486**After adaptation** 1487 1488```typescript 1489class A { 1490 value: string; 1491 constructor (value: string) { 1492 this.value = value; 1493 } 1494 1495 foo() { 1496 this.fooApply(this); 1497 } 1498 1499 fooApply(a: A) { 1500 console.log(a.value); 1501 } 1502} 1503 1504let a1 = new A('1'); 1505let a2 = new A('2'); 1506 1507a1.foo(); 1508a1.fooApply(a2); 1509``` 1510 1511## arkts-limited-stdlib 1512 1513### `Object.fromEntries()` 1514 1515**Before adaptation** 1516 1517```typescript 1518let entries = new Map([ 1519 ['foo', 123], 1520 ['bar', 456] 1521]); 1522 1523let obj = Object.fromEntries(entries); 1524``` 1525 1526**After adaptation** 1527 1528```typescript 1529let entries = new Map([ 1530 ['foo', 123], 1531 ['bar', 456] 1532]); 1533 1534let obj: Record<string, Object> = {}; 1535entries.forEach((value, key) => { 1536 if (key != undefined && key != null) { 1537 obj[key] = value; 1538 } 1539}) 1540``` 1541 1542### Using Properties and Methods of Number 1543 1544ArkTS does not allow the use of the following properties and methods for global objects: **Infinity**, **NaN**, **isFinite**, **isNaN**, **parseFloat**, and **parseInt**. 1545 1546You can use them for **Number**. 1547 1548**Before adaptation** 1549 1550```typescript 1551NaN; 1552isFinite(123); 1553parseInt('123'); 1554``` 1555 1556**After adaptation** 1557 1558```typescript 1559Number.NaN; 1560Number.isFinite(123); 1561Number.parseInt('123'); 1562``` 1563 1564## arkts-strict-typing(StrictModeError) 1565 1566### strictPropertyInitialization 1567 1568**Before adaptation** 1569 1570```typescript 1571interface I { 1572 name:string 1573} 1574 1575class A {} 1576 1577class Test { 1578 a: number; 1579 b: string; 1580 c: boolean; 1581 d: I; 1582 e: A; 1583} 1584 1585``` 1586 1587**After adaptation** 1588 1589```typescript 1590interface I { 1591 name:string 1592} 1593 1594class A {} 1595 1596class Test { 1597 a: number; 1598 b: string; 1599 c: boolean; 1600 d: I = { name:'abc' }; 1601 e: A | null = null; 1602 constructor(a:number, b:string, c:boolean) { 1603 this.a = a; 1604 this.b = b; 1605 this.c = c; 1606 } 1607} 1608 1609``` 1610### Type `*** | null` is not assignable to type `***` 1611 1612**Before adaptation** 1613 1614```typescript 1615class A { 1616 bar() {} 1617} 1618function foo(n: number) { 1619 if (n === 0) { 1620 return null; 1621 } 1622 return new A(); 1623} 1624function getNumber() { 1625 return 5; 1626} 1627let a:A = foo(getNumber()); 1628a.bar(); 1629``` 1630 1631**After adaptation** 1632 1633```typescript 1634class A { 1635 bar() {} 1636} 1637function foo(n: number) { 1638 if (n === 0) { 1639 return null; 1640 } 1641 return new A(); 1642} 1643function getNumber() { 1644 return 5; 1645} 1646 1647let a: A | null = foo(getNumber()); 1648a?.bar(); 1649``` 1650 1651### Strict Property Initialization Check 1652 1653In a class, if a property is not initialized and is not assigned a value in the constructor, ArkTS reports an error. 1654 1655**After adaptation** 1656 16571. Whenever possible, initialize properties during declaration based on service logic or assign values to the properties in constructors. Example: 1658 1659```typescript 1660//code with error 1661class Test { 1662 value: number 1663 flag: boolean 1664} 1665 1666// Method 1: Initialize properties during declaration. 1667class Test { 1668 value: number = 0 1669 flag: boolean = false 1670} 1671 1672// Method 2: Assign values to properties in the constructor. 1673class Test { 1674 value: number 1675 flag: boolean 1676 constructor(value: number, flag: boolean) { 1677 this.value = value; 1678 this.flag = flag; 1679 } 1680} 1681``` 1682 16832. 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: 1684 1685 Mode (i): **prop: A | null = null** 1686 1687 Mode (ii): **prop?:A** 1688 1689 Mode 3 (iii): **prop: A | undefined = undefined** 1690 1691- 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 runtime. 1692- In terms of code readability and simplicity, **prop?:A** is the syntax sugar of **prop: A | undefined = undefined**. You are advised to use optional properties. 1693 1694### Strict Function Type Check 1695 1696**Before adaptation** 1697 1698```typescript 1699function foo(fn: (value?: string) => void, value: string): void {} 1700 1701foo((value: string) => {}, ''); //error 1702``` 1703 1704**After adaptation** 1705 1706```typescript 1707function foo(fn: (value?: string) => void, value: string): void {} 1708 1709foo((value?: string) => {}, ''); 1710``` 1711 1712**Reason for change** 1713 1714In the following example, if strict function type check is not enabled during compilation, the code can be compiled successfully, but unexpected behavior occurs at runtime. 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.info(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 runtime. 1715 1716```typescript 1717function foo(fn: (value?: string) => void, value: string): void { 1718 let v: string | undefined = undefined; 1719 fn(v); 1720} 1721 1722foo((value: string) => { console.info(value.toUpperCase()) }, ''); // Cannot read properties of undefined (reading 'toUpperCase') 1723``` 1724 1725To avoid unexpected behavior during runtime, if strict type check is enabled, the preceding code cannot be compiled. You need to modify the code to ensure program security. 1726 1727### Strict Null Check 1728 1729**Before adaptation** 1730 1731```typescript 1732class Test { 1733 private value?: string 1734 1735 public printValue () { 1736 console.log(this.value.toLowerCase()); 1737 } 1738} 1739 1740let t = new Test(); 1741t.printValue(); 1742``` 1743 1744**After adaptation** 1745 1746When 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. 1747 1748```typescript 1749class Test { 1750 private value?: string 1751 1752 public printValue () { 1753 if (this.value) { 1754 console.log(this.value.toLowerCase()); 1755 } 1756 } 1757} 1758 1759let t = new Test(); 1760t.printValue(); 1761``` 1762 1763**Reason for change** 1764 1765In 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 runtime. 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 runtime, enable strict null check during compilation. 1766 1767### Function Return Type Mismatch 1768 1769**Before adaptation** 1770 1771```typescript 1772class Test { 1773 handleClick: (action: string, externInfo?: string) => void | null = null; 1774} 1775``` 1776 1777**After adaptation** 1778 1779In the original code, the return type of the function is parsed as **void | undefined**. Add parentheses to distinguish the union type. 1780 1781```typescript 1782class Test { 1783 handleClick: ((action: string, externInfo?: string) => void) | null = null; 1784} 1785``` 1786 1787### '***' is of type 'unknown' 1788 1789**Before adaptation** 1790 1791```typescript 1792try { 1793 1794} catch (error) { 1795 console.log(error.message); 1796} 1797``` 1798 1799**After adaptation** 1800 1801```typescript 1802import { BusinessError } from '@kit.BasicServicesKit' 1803 1804try { 1805 1806} catch (error) { 1807 console.log((error as BusinessError).message); 1808} 1809``` 1810 1811### Type '*** | null' is not assignable to type '\*\*\*' 1812 1813**Before adaptation** 1814 1815```typescript 1816class A { 1817 value: number 1818 constructor(value: number) { 1819 this.value = value; 1820 } 1821} 1822 1823function foo(v: number): A | null { 1824 if (v > 0) { 1825 return new A(v); 1826 } 1827 return null; 1828} 1829 1830let a: A = foo(); 1831``` 1832 1833**After adaptation: mode 1** 1834 1835Change the type of variable **a** to **let a: A | null = foo()**. 1836 1837```typescript 1838class A { 1839 value: number 1840 constructor(value: number) { 1841 this.value = value; 1842 } 1843} 1844 1845function foo(v: number): A | null { 1846 if (v > 0) { 1847 return new A(v); 1848 } 1849 return null; 1850} 1851 1852let a: A | null = foo(123); 1853 1854if (a != null) { 1855 // Non-empty branch 1856} else { 1857 // Process null. 1858} 1859``` 1860 1861**After adaptation: mode 2** 1862 1863If you can determine that a non-null value is returned when **foo** is called, you can use a non-null assertion operator **!**. 1864 1865```typescript 1866class A { 1867 value: number 1868 constructor(value: number) { 1869 this.value = value; 1870 } 1871} 1872 1873function foo(v: number): A | null { 1874 if (v > 0) { 1875 return new A(v); 1876 } 1877 return null; 1878} 1879 1880let a: A = foo(123)!; 1881``` 1882 1883### Cannot invoke an object which possibly 'undefined' 1884 1885**Before adaptation** 1886 1887```typescript 1888interface A { 1889 foo?: () => void 1890} 1891 1892let a:A = { foo: () => {} }; 1893a.foo(); 1894``` 1895 1896**After adaptation: mode 1** 1897 1898```typescript 1899interface A { 1900 foo: () => void 1901} 1902let a: A = { foo: () => {} }; 1903a.foo(); 1904``` 1905 1906**After adaptation: mode 2** 1907 1908```typescript 1909interface A { 1910 foo?: () => void 1911} 1912 1913let a: A = { foo: () => {} }; 1914if (a.foo) { 1915 a.foo(); 1916} 1917``` 1918 1919**Reason for change** 1920 1921In 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 to set **foo** as an optional property based on the service logic. If defining an optional property is necessary, a null check is required for accessing the property. 1922 1923### Variable '***' is used before being assigned 1924 1925**Before adaptation** 1926 1927```typescript 1928class Test { 1929 value: number = 0 1930} 1931 1932let a: Test 1933try { 1934 a = { value: 1}; 1935} catch (e) { 1936 a.value; 1937} 1938a.value; 1939``` 1940 1941**After adaptation** 1942 1943```typescript 1944class Test { 1945 value: number = 0 1946} 1947 1948let a: Test | null = null; 1949try { 1950 a = { value:1 }; 1951} catch (e) { 1952 if (a) { 1953 a.value; 1954 } 1955} 1956 1957if (a) { 1958 a.value; 1959} 1960``` 1961 1962**Reason for change** 1963 1964For primitive types, a value can be assigned based on the service logic, for example, **0**, **''**, and **false**. 1965 1966For object types, 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. 1967 1968### Function lacks ending return statement and return type does not include 'undefined'. 1969 1970**Before adaptation** 1971 1972```typescript 1973function foo(a: number): number { 1974 if (a > 0) { 1975 return a; 1976 } 1977} 1978``` 1979 1980**After adaptation: mode 1** 1981 1982Return a proper value in the **else** branch based on the service logic. 1983 1984**After adaptation: mode 2** 1985 1986```typescript 1987function foo(a: number): number | undefined { 1988 if (a > 0) { 1989 return a; 1990 } 1991 return 1992} 1993``` 1994 1995## arkts-strict-typing-required 1996 1997Remove the @ts-nocheck comment and explicitly declare types for all variables. 1998 1999**Before adaptation** 2000 2001```typescript 2002// @ts-nocheck 2003var a: any = 123; 2004``` 2005 2006**After adaptation** 2007 2008```typescript 2009let a: number = 123; 2010``` 2011 2012**Reason for change** 2013 2014ArkTS 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. 2015 2016## Importing ArkTS files to JS and TS files is not allowed 2017 2018## arkts-no-tsdeps 2019 2020In .ts and .js files, it is not allowed to import source code from an .ets file. 2021 2022**After adaptation** 2023 2024Mode 1: Change the file name extension of the .ts file to .ets and adapt the code based on the ArkTS syntax rules. 2025 2026Mode 2: Extract the code that the .ts file depends on from the .ets file to the .ts file. 2027 2028## arkts-no-special-imports 2029 2030Use **import { ... } from '...'** to import the type. 2031 2032**Before adaptation** 2033 2034```typescript 2035import type {A, B, C, D } from '***' 2036``` 2037 2038 2039**After adaptation** 2040 2041```typescript 2042import {A, B, C, D } from '***' 2043``` 2044 2045## arkts-no-classes-as-obj 2046 2047### Using Class to Construct an Instance 2048 2049**Before adaptation** 2050 2051```typescript 2052class Controller { 2053 value: string = '' 2054 constructor(value: string) { 2055 this.value = value 2056 } 2057} 2058 2059interface ControllerConstructor { 2060 new (value: string): Controller; 2061} 2062 2063class Menu { 2064 controller: ControllerConstructor = Controller 2065 createController() { 2066 if (this.controller) { 2067 return new this.controller('abc'); 2068 } 2069 return null; 2070 } 2071} 2072 2073let t = new Menu(); 2074console.log(t.createController()!.value); 2075``` 2076 2077**After adaptation** 2078 2079```typescript 2080class Controller { 2081 value: string = '' 2082 constructor(value: string) { 2083 this.value = value 2084 } 2085} 2086 2087type ControllerConstructor = () => Controller; 2088 2089class Menu { 2090 controller: ControllerConstructor = () => { return new Controller('abc'); } 2091 createController() { 2092 if (this.controller) { 2093 return this.controller(); 2094 } 2095 return null; 2096 } 2097} 2098 2099let t: Menu = new Menu(); 2100console.log(t.createController()!.value); 2101``` 2102 2103### Accessing Static Properties 2104 2105**Before adaptation** 2106 2107```typescript 2108class C1 { 2109 static value: string = 'abc' 2110} 2111 2112class C2 { 2113 static value: string = 'def' 2114} 2115 2116function getValue(obj: any) { 2117 return obj['value']; 2118} 2119 2120console.log(getValue(C1)); 2121console.log(getValue(C2)); 2122``` 2123 2124**After adaptation** 2125 2126```typescript 2127class C1 { 2128 static value: string = 'abc' 2129} 2130 2131class C2 { 2132 static value: string = 'def' 2133} 2134 2135function getC1Value(): string { 2136 return C1.value; 2137} 2138 2139function getC2Value(): string { 2140 return C2.value; 2141} 2142 2143console.log(getC1Value()); 2144console.log(getC2Value()); 2145``` 2146 2147## arkts-no-side-effects-imports 2148 2149Use dynamic imports. 2150 2151**Before adaptation** 2152 2153```typescript 2154import 'module' 2155``` 2156 2157**After adaptation** 2158 2159```typescript 2160import('module') 2161``` 2162 2163## arkts-no-func-props 2164 2165Use a **class** to organize multiple related functions. 2166 2167**Before adaptation** 2168 2169```typescript 2170function foo(value: number): void { 2171 console.log(value.toString()); 2172} 2173 2174foo.add = (left: number, right: number) => { 2175 return left + right; 2176} 2177 2178foo.sub = (left: number, right: number) => { 2179 return left - right; 2180} 2181``` 2182 2183**After adaptation** 2184 2185```typescript 2186class Foo { 2187 static foo(value: number): void { 2188 console.log(value.toString()); 2189 } 2190 2191 static add(left: number, right: number): number { 2192 return left + right; 2193 } 2194 2195 static sub(left: number, right: number): number { 2196 return left - right; 2197 } 2198} 2199``` 2200 2201## arkts-limited-esobj 2202 2203Use specific types (such as **number**, **string**) or **interface** instead of the ambiguous **ESObject**. 2204 2205**Before adaptation** 2206 2207```typescript 2208// lib.d.ts 2209declare function foo(): any; 2210 2211// main.ets 2212let e0: ESObject = foo(); 2213 2214function f() { 2215 let e1 = foo(); 2216 let e2: ESObject = 1; 2217 let e3: ESObject = {}; 2218 let e4: ESObject = ''; 2219} 2220``` 2221 2222**After adaptation** 2223 2224```typescript 2225// lib.d.ts 2226declare function foo(): any; 2227 2228// main.ets 2229interface I {} 2230 2231function f() { 2232 let e0: ESObject = foo(); 2233 let e1: ESObject = foo(); 2234 let e2: number = 1; 2235 let e3: I = {}; 2236 let e4: string = ''; 2237} 2238``` 2239 2240## Copy 2241 2242### Shallow Copy 2243 2244**TypeScript** 2245 2246```typescript 2247function shallowCopy(obj: object): object { 2248 let newObj = {}; 2249 Object.assign(newObj, obj); 2250 return newObj; 2251} 2252``` 2253 2254**ArkTS** 2255 2256```typescript 2257function shallowCopy(obj: object): object { 2258 let newObj: Record<string, Object> = {}; 2259 for (let key of Object.keys(obj)) { 2260 newObj[key] = obj[key]; 2261 } 2262 return newObj; 2263} 2264``` 2265 2266### Deep Copy 2267 2268**TypeScript** 2269 2270```typescript 2271function deepCopy(obj: object): object { 2272 let newObj = Array.isArray(obj) ? [] : {}; 2273 for (let key in obj) { 2274 if (typeof obj[key] === 'object') { 2275 newObj[key] = deepCopy(obj[key]); 2276 } else { 2277 newObj[key] = obj[key]; 2278 } 2279 } 2280 return newObj; 2281} 2282``` 2283 2284**ArkTS** 2285 2286```typescript 2287function deepCopy(obj: object): object { 2288 let newObj: Record<string, Object> | Object[] = Array.isArray(obj) ? [] : {}; 2289 for (let key of Object.keys(obj)) { 2290 if (typeof obj[key] === 'object') { 2291 newObj[key] = deepCopy(obj[key]); 2292 } else { 2293 newObj[key] = obj[key]; 2294 } 2295 } 2296 return newObj; 2297} 2298``` 2299 2300## Typical Application Scenarios of State Management 2301 2302### Using State Variables Outside of Structs 2303 2304The 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. 2305 2306**Not recommended** 2307 2308```typescript 2309export class MyComponentController { 2310 item: MyComponent = null; 2311 2312 setItem(item: MyComponent) { 2313 this.item = item; 2314 } 2315 2316 changeText(value: string) { 2317 this.item.value = value; 2318 } 2319} 2320 2321@Component 2322export default struct MyComponent { 2323 public controller: MyComponentController = null; 2324 @State value: string = 'Hello World'; 2325 2326 build() { 2327 Column() { 2328 Text(this.value) 2329 .fontSize(50) 2330 } 2331 } 2332 2333 aboutToAppear() { 2334 if (this.controller) 2335 this.controller.setItem(this); // You are not advised to pass this as a parameter to the outside struct. 2336 } 2337} 2338 2339@Entry 2340@Component 2341struct ObjThisOldPage { 2342 controller = new MyComponentController(); 2343 2344 build() { 2345 Column() { 2346 MyComponent({ controller: this.controller }) 2347 Button('change value').onClick(() => { 2348 this.controller.changeText('Text'); 2349 }) 2350 } 2351 } 2352} 2353``` 2354 2355**Recommended** 2356 2357```typescript 2358class CC { 2359 value: string = '1'; 2360 2361 constructor(value: string) { 2362 this.value = value; 2363 } 2364} 2365 2366export class MyComponentController { 2367 item: CC = new CC('1'); 2368 2369 setItem(item: CC) { 2370 this.item = item; 2371 } 2372 2373 changeText(value: string) { 2374 this.item.value = value; 2375 } 2376} 2377 2378@Component 2379export default struct MyComponent { 2380 public controller: MyComponentController | null = null; 2381 @State value: CC = new CC('Hello World'); 2382 2383 build() { 2384 Column() { 2385 Text(`${this.value.value}`) 2386 .fontSize(50) 2387 } 2388 } 2389 2390 aboutToAppear() { 2391 if (this.controller) 2392 this.controller.setItem(this.value); 2393 } 2394} 2395 2396@Entry 2397@Component 2398struct StyleExample { 2399 controller: MyComponentController = new MyComponentController(); 2400 2401 build() { 2402 Column() { 2403 MyComponent({ controller: this.controller }) 2404 Button('change value').onClick(() => { 2405 this.controller.changeText('Text'); 2406 }) 2407 } 2408 } 2409} 2410``` 2411 2412### Using Union Types in Structs 2413 2414The 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. 2415 2416**Not recommended** 2417 2418```typescript 2419class Data { 2420 aa: number = 11; 2421} 2422 2423@Entry 2424@Component 2425struct DatauionOldPage { 2426 @State array: Data[] = [new Data(), new Data(), new Data()]; 2427 2428 @Builder 2429 componentCloser(data: Data) { 2430 Text(data.aa + '').fontSize(50) 2431 } 2432 2433 build() { 2434 Row() { 2435 Column() { 2436 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2437 } 2438 .width('100%') 2439 } 2440 .height('100%') 2441 } 2442} 2443 2444@Component 2445export struct ForEachCom { 2446 arrayList: any[]; // The struct does not support generics. An arkts-no-any-unknown error is reported. 2447 @BuilderParam closer: (data: any) => void = this.componentCloser; // The struct does not support generics. An arkts-no-any-unknown error is reported. 2448 2449 @Builder 2450 componentCloser() { 2451 } 2452 2453 build() { 2454 Column() { 2455 ForEach(this.arrayList, (item: any) => { // The struct does not support generics. An arkts-no-any-unknown error is reported. 2456 Row() { 2457 this.closer(item) 2458 }.width('100%').height(200).backgroundColor('#eee') 2459 }) 2460 } 2461 } 2462} 2463``` 2464 2465**Recommended** 2466 2467```typescript 2468class Data { 2469 aa: number = 11; 2470} 2471 2472class Model { 2473 aa: string = '11'; 2474} 2475 2476type UnionData = Data | Model; 2477 2478@Entry 2479@Component 2480struct DatauionPage { 2481 array: UnionData[] = [new Data(), new Data(), new Data()]; 2482 2483 @Builder 2484 componentCloser(data: UnionData) { 2485 if (data instanceof Data) { 2486 Text(data.aa + '').fontSize(50) 2487 } 2488 } 2489 2490 build() { 2491 Row() { 2492 Column() { 2493 ForEachCom({ arrayList: this.array, closer: this.componentCloser }) 2494 } 2495 .width('100%') 2496 } 2497 .height('100%') 2498 } 2499} 2500 2501@Component 2502export struct ForEachCom { 2503 arrayList: UnionData[] = [new Data(), new Data(), new Data()]; 2504 @BuilderParam closer: (data: UnionData) => void = this.componentCloser; 2505 2506 @Builder 2507 componentCloser() { 2508 } 2509 2510 build() { 2511 Column() { 2512 ForEach(this.arrayList, (item: UnionData) => { 2513 Row() { 2514 this.closer(item) 2515 }.width('100%').height(200).backgroundColor('#eee') 2516 }) 2517 } 2518 } 2519} 2520``` 2521