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