• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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