1# 适配指导案例
2
3本文通过更多应用场景中的案例,提供在ArkTS语法规则下将TS代码适配成ArkTS代码的建议。各章以ArkTS语法规则英文名称命名,每个案例提供适配前的TS代码和适配后的ArkTS代码。
4
5## arkts-identifiers-as-prop-names
6
7**应用代码**
8
9```typescript
10interface W {
11  bundleName: string
12  action: string
13  entities: string[]
14}
15
16let wantInfo: W = {
17  'bundleName': 'com.huawei.hmos.browser',
18  'action': 'ohos.want.action.viewData',
19  'entities': ['entity.system.browsable']
20}
21```
22
23**建议改法**
24
25```typescript
26interface W {
27  bundleName: string
28  action: string
29  entities: string[]
30}
31
32let wantInfo: W = {
33  bundleName: 'com.huawei.hmos.browser',
34  action: 'ohos.want.action.viewData',
35  entities: ['entity.system.browsable']
36}
37```
38
39## arkts-no-any-unknown
40
41### 按照业务逻辑,将代码中的`any, unknown`改为具体的类型
42
43```typescript
44function printObj(obj: any) {
45  console.log(obj);
46}
47
48printObj('abc');
49```
50
51**建议改法**
52
53```typescript
54function printObj(obj: string) {
55  console.log(obj);
56}
57
58printObj('abc');
59```
60
61### 标注JSON.parse返回值类型
62
63**应用代码**
64
65```typescript
66class A {
67  v: number = 0
68  s: string = ''
69
70  foo(str: string) {
71    let tmpStr = JSON.parse(str);
72    if (tmpStr.add != undefined) {
73      this.v = tmpStr.v;
74      this.s = tmpStr.s;
75    }
76  }
77}
78```
79
80**建议改法**
81
82```typescript
83class A {
84  v: number = 0
85  s: string = ''
86
87  foo(str: string) {
88    let tmpStr: Record<string, Object> = JSON.parse(str);
89    if (tmpStr.add != undefined) {
90      this.v = tmpStr.v as number;
91      this.s = tmpStr.s as string;
92    }
93  }
94}
95```
96
97### 使用Record类型
98
99**应用代码**
100
101```typescript
102function printProperties(obj: any) {
103  console.log(obj.name);
104  console.log(obj.value);
105}
106```
107
108**建议改法**
109
110```typescript
111function printProperties(obj: Record<string, Object>) {
112  console.log(obj.name as string);
113  console.log(obj.value as string);
114}
115```
116
117## arkts-no-call-signature
118
119使用函数类型来替代。
120
121**应用代码**
122
123```typescript
124interface I {
125  (value: string): void;
126}
127
128function foo(fn: I) {
129  fn('abc');
130}
131
132foo((value: string) => {
133  console.log(value);
134})
135```
136
137
138**建议改法**
139
140```typescript
141type I = (value: string) => void
142
143function foo(fn: I) {
144  fn('abc');
145}
146
147foo((value: string) => {
148  console.log(value);
149})
150```
151
152## arkts-no-ctor-signatures-type
153
154**应用代码**
155
156```typescript
157class Controller {
158  value: string = ''
159
160  constructor(value: string) {
161    this.value = value;
162  }
163}
164
165type ControllerConstructor = {
166  new (value: string): Controller;
167}
168
169class Menu {
170  controller: ControllerConstructor = Controller
171  createController() {
172    if (this.controller) {
173      return new this.controller(123);
174    }
175    return null;
176  }
177}
178
179let t = new Menu();
180console.log(t.createController()!.value);
181```
182
183**建议改法**
184
185```typescript
186class Controller {
187  value: string = ''
188
189  constructor(value: string) {
190    this.value = value;
191  }
192}
193
194type ControllerConstructor = () => Controller;
195
196class Menu {
197  controller: ControllerConstructor = () => {
198    return new Controller('abc');
199  }
200
201  createController() {
202    if (this.controller) {
203      return this.controller();
204    }
205    return null;
206  }
207}
208
209let t: Menu = new Menu();
210console.log(t.createController()!.value);
211```
212
213## arkts-no-indexed-signatures
214
215使用Record类型来替代。
216
217**应用代码**
218
219```typescript
220function foo(data: { [key: string]: string }) {
221  data['a'] = 'a';
222  data['b'] = 'b';
223  data['c'] = 'c';
224}
225```
226
227**建议改法**
228
229```typescript
230function foo(data: Record<string, string>) {
231  data['a'] = 'a';
232  data['b'] = 'b';
233  data['c'] = 'c';
234}
235```
236
237## arkts-no-typing-with-this
238
239**应用代码**
240
241```typescript
242class C {
243  getInstance(): this {
244    return this;
245  }
246}
247```
248
249**建议改法**
250
251```typescript
252class C {
253  getInstance(): C {
254    return this;
255  }
256}
257```
258
259## arkts-no-ctor-prop-decls
260
261**应用代码**
262
263```typescript
264class Person {
265  constructor(readonly name: string) {}
266
267  getName(): string {
268    return this.name;
269  }
270}
271```
272
273**建议改法**
274
275```typescript
276class Person {
277  name: string
278  constructor(name: string) {
279    this.name = name;
280  }
281
282  getName(): string {
283    return this.name;
284  }
285}
286```
287
288## arkts-no-ctor-signatures-iface
289
290**应用代码**
291
292```typescript
293class Controller {
294  value: string = ''
295
296  constructor(value: string) {
297    this.value = value;
298  }
299}
300
301interface ControllerConstructor {
302  new (value: string): Controller;
303}
304
305class Menu {
306  controller: ControllerConstructor = Controller
307  createController() {
308    if (this.controller) {
309      return new this.controller('abc');
310    }
311    return null;
312  }
313}
314
315let t = new Menu();
316console.log(t.createController()!.value);
317```
318
319**建议改法**
320
321```typescript
322class Controller {
323  value: string = ''
324
325  constructor(value: string) {
326    this.value = value;
327  }
328}
329
330type ControllerConstructor = () => Controller;
331
332class Menu {
333  controller: ControllerConstructor = () => {
334    return new Controller('abc');
335  }
336
337  createController() {
338    if (this.controller) {
339      return this.controller();
340    }
341    return null;
342  }
343}
344
345let t: Menu = new Menu();
346console.log(t.createController()!.value);
347```
348
349## arkts-no-props-by-index
350
351可以转换成Record类型,用来访问对象的属性。
352
353**应用代码**
354
355```typescript
356import { router } from '@kit.ArkUI';
357let params: Object = router.getParams();
358let funNum: number = params['funNum'];
359let target: string = params['target'];
360```
361
362**建议改法**
363
364```typescript
365import { router } from '@kit.ArkUI';
366let params = router.getParams() as Record<string, string | number>;
367let funNum: number = params.funNum as number;
368let target: string = params.target as string;
369```
370
371## arkts-no-inferred-generic-params
372
373**应用代码**
374
375```typescript
376class A {
377  str: string = ''
378}
379class B extends A {}
380class C extends A {}
381
382let arr: Array<A> = [];
383
384let originMenusMap:Map<string, C> = new Map(arr.map(item => [item.str, (item instanceof C) ? item: null]));
385```
386
387**建议改法**
388
389```typescript
390class A {
391  str: string = ''
392}
393class B extends A {}
394class C extends A {}
395
396let arr: Array<A> = [];
397
398let originMenusMap: Map<string, C | null> = new Map<string, C | null>(arr.map<[string, C | null]>(item => [item.str, (item instanceof C) ? item: null]));
399```
400
401**原因**
402
403`(item instanceof C) ? item: null` 需要声明类型为`C | null`,由于编译器无法推导出`map`的泛型类型参数,需要显式标注。
404
405## arkts-no-regexp-literals
406
407**应用代码**
408
409```typescript
410let regex: RegExp = /\s*/g;
411```
412
413**建议改法**
414
415```typescript
416let regexp: RegExp = new RegExp('\\s*','g');
417```
418
419**原因**
420
421如果正则表达式中使用了标志符,需要将其作为`new RegExp()`的参数。
422
423## arkts-no-untyped-obj-literals
424
425### 从SDK中导入类型,标注object literal类型
426
427**应用代码**
428
429```typescript
430const area = {
431  pixels: new ArrayBuffer(8),
432  offset: 0,
433  stride: 8,
434  region: { size: { height: 1,width:2 }, x: 0, y: 0 }
435}
436```
437
438**建议改法**
439
440```typescript
441import { image } from '@kit.ImageKit';
442
443const area: image.PositionArea = {
444  pixels: new ArrayBuffer(8),
445  offset: 0,
446  stride: 8,
447  region: { size: { height: 1, width: 2 }, x: 0, y: 0 }
448}
449```
450
451### 用class为object literal标注类型,需要class的构造函数无参数
452
453**应用代码**
454
455```typescript
456class Test {
457  value: number = 1
458
459  constructor(value: number) {
460    this.value = value;
461  }
462}
463
464let t: Test = { value: 2 };
465```
466
467**建议改法1**
468
469```typescript
470// 去除构造函数
471class Test {
472  value: number = 1
473}
474
475let t: Test = { value: 2 };
476```
477
478**建议改法2**
479```typescript
480// 使用new
481class Test {
482  value: number = 1
483
484  constructor(value: number) {
485    this.value = value;
486  }
487}
488
489let t: Test = new Test(2);
490```
491
492**原因**
493
494```typescript
495class C {
496  value: number = 1
497
498  constructor(n: number) {
499    if (n < 0) {
500      throw new Error('Negative');
501    }
502    this.value = n;
503  }
504}
505
506let s: C = new C(-2); 	//抛出异常
507let t: C = { value: -2 };	//ArkTS不支持
508```
509
510例如在上面的例子中,如果允许使用`C`来标注object literal的类型,那么上述代码中的变量`t`会导致行为的二义性。ArkTS禁止通过object literal来绕过这一行为。
511
512### 用class/interface为object literal标注类型,需要使用identifier作为object literal的key
513
514**应用代码**
515
516```typescript
517class Test {
518  value: number = 0
519}
520
521let arr: Test[] = [
522  {
523    'value': 1
524  },
525  {
526    'value': 2
527  },
528  {
529    'value': 3
530  }
531]
532```
533
534**建议改法**
535
536```typescript
537class Test {
538  value: number = 0
539}
540let arr: Test[] = [
541  {
542    value: 1
543  },
544  {
545    value: 2
546  },
547  {
548    value: 3
549  }
550]
551```
552
553### 使用Record为object literal标注类型,需要使用字符串作为object literal的key
554
555**应用代码**
556
557```typescript
558let obj: Record<string, number | string> = {
559  value: 123,
560  name: 'abc'
561}
562```
563
564**建议改法**
565
566```typescript
567let obj: Record<string, number | string> = {
568  'value': 123,
569  'name': 'abc'
570}
571```
572
573### 函数参数类型包含index signature
574
575**应用代码**
576
577```typescript
578function foo(obj: { [key: string]: string}): string {
579  if (obj != undefined && obj != null) {
580    return obj.value1 + obj.value2;
581  }
582  return '';
583}
584```
585
586**建议改法**
587
588```typescript
589function foo(obj: Record<string, string>): string {
590  if (obj != undefined && obj != null) {
591    return obj.value1 + obj.value2;
592  }
593  return '';
594}
595```
596
597### 函数实参使用了object literal
598
599**应用代码**
600
601```typescript
602(fn) => {
603  fn({ value: 123, name:'' });
604}
605```
606
607**建议改法**
608
609```typescript
610class T {
611  value: number = 0
612  name: string = ''
613}
614
615(fn: (v: T) => void) => {
616  fn({ value: 123, name: '' });
617}
618```
619
620### class/interface 中包含方法
621
622**应用代码**
623
624```typescript
625interface T {
626  foo(value: number): number
627}
628
629let t:T = { foo: (value) => { return value } };
630```
631
632**建议改法1**
633
634```typescript
635interface T {
636  foo: (value: number) => number
637}
638
639let t:T = { foo: (value) => { return value } };
640```
641
642**建议改法2**
643
644```typescript
645class T {
646  foo: (value: number) => number = (value: number) => {
647    return value;
648  }
649}
650
651let t:T = new T();
652```
653
654**原因**
655
656class/interface中声明的方法应该被所有class的实例共享。ArkTS不支持通过object literal改写实例方法。ArkTS支持函数类型的属性。
657
658### export default对象
659
660**应用代码**
661
662```typescript
663export default {
664  onCreate() {
665    // ...
666  },
667  onDestroy() {
668    // ...
669  }
670}
671```
672
673**建议改法**
674
675```typescript
676class Test {
677  onCreate() {
678    // ...
679  }
680  onDestroy() {
681    // ...
682  }
683}
684
685export default new Test()
686```
687
688### 通过导入namespace获取类型
689
690**应用代码**
691
692```typescript
693// test.d.ets
694declare namespace test {
695  interface I {
696    id: string;
697    type: number;
698  }
699
700  function foo(name: string, option: I): void;
701}
702
703export default test;
704
705// app.ets
706import { test } from 'test';
707
708let option = { id: '', type: 0 };
709test.foo('', option);
710```
711
712**建议改法**
713
714```typescript
715// test.d.ets
716declare namespace test {
717  interface I {
718    id: string;
719    type: number;
720  }
721
722  function foo(name: string, option: I): void;
723}
724
725export default test;
726
727// app.ets
728import { test } from 'test';
729
730let option: test.I = { id: '', type: 0 };
731test.foo('', option);
732```
733
734**原因**
735
736对象字面量缺少类型,根据`test.foo`分析可以得知,`option`的类型来源于声明文件,那么只需要将类型导入即可。
737注意到在`test.d.ets`中,`I`是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。
738
739### object literal传参给Object类型
740
741**应用代码**
742
743```typescript
744function emit(event: string, ...args: Object[]): void {}
745
746emit('', {
747  'action': 11,
748  'outers': false
749});
750```
751
752**建议改法**
753
754```typescript
755function emit(event: string, ...args: Object[]): void {}
756
757let emitArg: Record<string, number | boolean> = {
758   'action': 11,
759   'outers': false
760}
761
762emit('', emitArg);
763```
764
765## arkts-no-obj-literals-as-types
766
767**应用代码**
768
769```typescript
770type Person = { name: string, age: number }
771```
772
773**建议改法**
774
775```typescript
776interface Person {
777  name: string,
778  age: number
779}
780```
781
782## arkts-no-noninferrable-arr-literals
783
784**应用代码**
785
786```typescript
787let permissionList = [
788  { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
789  { name: '麦克风', value: '用于反馈问题单时增加语音' },
790  { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
791]
792```
793
794**建议改法**
795
796为对象字面量声明类型
797
798```typescript
799class PermissionItem {
800  name?: string
801  value?: string
802}
803
804let permissionList: PermissionItem[] = [
805  { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
806  { name: '麦克风', value: '用于反馈问题单时增加语音' },
807  { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
808]
809```
810
811## arkts-no-method-reassignment
812
813**应用代码**
814
815```typescript
816class C {
817  add(left: number, right: number): number {
818    return left + right;
819  }
820}
821
822function sub(left: number, right: number): number {
823  return left - right;
824}
825
826let c1 = new C();
827c1.add = sub;
828```
829
830**建议改法**
831
832```typescript
833class C {
834  add: (left: number, right: number) => number =
835    (left: number, right: number) => {
836      return left + right;
837    }
838}
839
840function sub(left: number, right: number): number {
841  return left - right;
842}
843
844let c1 = new C();
845c1.add = sub;
846```
847
848## arkts-no-polymorphic-unops
849
850**应用代码**
851
852```typescript
853let a = +'5';
854let b = -'5';
855let c = ~'5';
856let d = +'string';
857```
858
859**建议改法**
860
861```typescript
862let a = Number.parseInt('5');
863let b = -Number.parseInt('5');
864let c = ~Number.parseInt('5');
865let d = new Number('string');
866```
867
868## arkts-no-type-query
869
870**应用代码**
871
872```typescript
873// module1.ts
874class C {
875  value: number = 0
876}
877
878export let c = new C()
879
880// module2.ts
881import { c } from './module1'
882let t: typeof c = { value: 123 };
883```
884
885**建议改法**
886
887```typescript
888// module1.ts
889class C {
890  value: number = 0
891}
892
893export { C }
894
895// module2.ts
896import { C } from './module1'
897let t: C = { value: 123 };
898```
899
900## arkts-no-in
901
902### 使用Object.keys判断属性是否存在
903
904**应用代码**
905
906```typescript
907function test(str: string, obj: Record<string, Object>) {
908  return str in obj;
909}
910```
911
912**建议改法**
913
914```typescript
915function test(str: string, obj: Record<string, Object>) {
916  for (let i of Object.keys(obj)) {
917    if (i == str) {
918      return true;
919    }
920  }
921  return false;
922}
923```
924
925## arkts-no-destruct-assignment
926
927**应用代码**
928
929```typescript
930let map = new Map<string, string>([['a', 'a'], ['b', 'b']]);
931for (let [key, value] of map) {
932  console.log(key);
933  console.log(value);
934}
935```
936
937**建议改法**
938
939使用数组
940
941```typescript
942let map = new Map<string, string>([['a', 'a'], ['b', 'b']]);
943for (let arr of map) {
944  let key = arr[0];
945  let value = arr[1];
946  console.log(key);
947  console.log(value);
948}
949```
950
951## arkts-no-types-in-catch
952
953**应用代码**
954
955```typescript
956import { BusinessError } from '@kit.BasicServicesKit'
957
958try {
959  // ...
960} catch (e: BusinessError) {
961  console.error(e.message, e.code);
962}
963```
964
965**建议改法**
966
967```typescript
968import { BusinessError } from '@kit.BasicServicesKit'
969
970try {
971  // ...
972} catch (error) {
973  let e: BusinessError = error as BusinessError;
974  console.error(e.message, e.code);
975}
976```
977
978## arkts-no-for-in
979
980**应用代码**
981
982```typescript
983interface Person {
984  [name: string]: string
985}
986let p: Person = {
987  name: 'tom',
988  age: '18'
989};
990
991for (let t in p) {
992  console.log(p[t]);  // log: "tom", "18"
993}
994```
995
996**建议改法**
997
998```typescript
999let p: Record<string, string> = {
1000  'name': 'tom',
1001  'age': '18'
1002};
1003
1004for (let ele of Object.entries(p)) {
1005  console.log(ele[1]);  // log: "tom", "18"
1006}
1007```
1008
1009## arkts-no-mapped-types
1010
1011**应用代码**
1012
1013```typescript
1014class C {
1015  a: number = 0
1016  b: number = 0
1017  c: number = 0
1018}
1019type OptionsFlags = {
1020  [Property in keyof C]: string
1021}
1022```
1023
1024**建议改法**
1025
1026```typescript
1027class C {
1028  a: number = 0
1029  b: number = 0
1030  c: number = 0
1031}
1032
1033type OptionsFlags = Record<keyof C, string>
1034```
1035
1036## arkts-limited-throw
1037
1038**应用代码**
1039
1040```typescript
1041import { BusinessError } from '@kit.BasicServicesKit'
1042
1043function ThrowError(error: BusinessError) {
1044  throw error;
1045}
1046```
1047
1048**建议改法**
1049
1050```typescript
1051import { BusinessError } from '@kit.BasicServicesKit'
1052
1053function ThrowError(error: BusinessError) {
1054  throw error as Error;
1055}
1056```
1057
1058**原因**
1059
1060`throw`语句中值的类型必须为`Error`或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用`as`将类型转换为`Error`。
1061
1062## arkts-no-standalone-this
1063
1064### 函数内使用this
1065
1066**应用代码**
1067
1068```typescript
1069function foo() {
1070  console.log(this.value);
1071}
1072
1073let obj = { value: 'abc' };
1074foo.apply(obj);
1075```
1076
1077**建议改法1**
1078
1079使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制
1080
1081```typescript
1082class Test {
1083  value: string = ''
1084  constructor (value: string) {
1085    this.value = value
1086  }
1087
1088  foo() {
1089    console.log(this.value);
1090  }
1091}
1092
1093let obj: Test = new Test('abc');
1094obj.foo();
1095```
1096
1097**建议改法2**
1098
1099将this作为参数传入
1100
1101```typescript
1102function foo(obj: Test) {
1103  console.log(obj.value);
1104}
1105
1106class Test {
1107  value: string = ''
1108}
1109
1110let obj: Test = { value: 'abc' };
1111foo(obj);
1112```
1113
1114**建议改法3**
1115
1116将属性作为参数传入
1117```typescript
1118function foo(value: string) {
1119  console.log(value);
1120}
1121
1122class Test {
1123  value: string = ''
1124}
1125
1126let obj: Test = { value: 'abc' };
1127foo(obj.value);
1128```
1129
1130### class的静态方法内使用this
1131
1132**应用代码**
1133
1134```typescript
1135class Test {
1136  static value: number = 123
1137  static foo(): number {
1138    return this.value
1139  }
1140}
1141```
1142
1143**建议改法**
1144
1145```typescript
1146class Test {
1147  static value: number = 123
1148  static foo(): number {
1149    return Test.value
1150  }
1151}
1152```
1153
1154## arkts-no-spread
1155
1156**应用代码**
1157
1158```typescript
1159// test.d.ets
1160declare namespace test {
1161  interface I {
1162    id: string;
1163    type: number;
1164  }
1165
1166  function foo(): I;
1167}
1168
1169export default test
1170
1171// app.ets
1172import test from 'test';
1173
1174let t: test.I = {
1175  ...test.foo(),
1176  type: 0
1177}
1178```
1179
1180**建议改法**
1181
1182```typescript
1183// test.d.ets
1184declare namespace test {
1185  interface I {
1186    id: string;
1187    type: number;
1188  }
1189
1190  function foo(): I;
1191}
1192
1193export default test
1194
1195// app.ets
1196import test from 'test';
1197
1198let t: test.I = test.foo();
1199t.type = 0;
1200```
1201
1202**原因**
1203
1204ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。
1205
1206## arkts-no-ctor-signatures-funcs
1207
1208在class内声明属性,而不是在构造函数上。
1209
1210**应用代码**
1211
1212```typescript
1213class Controller {
1214  value: string = ''
1215  constructor(value: string) {
1216    this.value = value
1217  }
1218}
1219
1220type ControllerConstructor = new (value: string) => Controller;
1221
1222class Menu {
1223  controller: ControllerConstructor = Controller
1224  createController() {
1225    if (this.controller) {
1226      return new this.controller('abc');
1227    }
1228    return null;
1229  }
1230}
1231
1232let t = new Menu()
1233console.log(t.createController()!.value)
1234```
1235
1236**建议改法**
1237
1238```typescript
1239class Controller {
1240  value: string = ''
1241  constructor(value: string) {
1242    this.value = value;
1243  }
1244}
1245
1246type ControllerConstructor = () => Controller;
1247
1248class Menu {
1249  controller: ControllerConstructor = () => { return new Controller('abc') }
1250  createController() {
1251    if (this.controller) {
1252      return this.controller();
1253    }
1254    return null;
1255  }
1256}
1257
1258let t: Menu = new Menu();
1259console.log(t.createController()!.value);
1260```
1261
1262## arkts-no-globalthis
1263
1264由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。
1265
12661. 建议按照业务逻辑根据`import/export`语法实现数据在不同模块的传递。
1267
12682. 必要情况下,可以通过构造的**单例对象**来实现全局对象的功能。(**说明:**不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。)
1269
1270**构造单例对象**
1271
1272```typescript
1273// 构造单例对象
1274export class GlobalContext {
1275  private constructor() {}
1276  private static instance: GlobalContext;
1277  private _objects = new Map<string, Object>();
1278
1279  public static getContext(): GlobalContext {
1280    if (!GlobalContext.instance) {
1281      GlobalContext.instance = new GlobalContext();
1282    }
1283    return GlobalContext.instance;
1284  }
1285
1286  getObject(value: string): Object | undefined {
1287    return this._objects.get(value);
1288  }
1289
1290  setObject(key: string, objectClass: Object): void {
1291    this._objects.set(key, objectClass);
1292  }
1293}
1294
1295```
1296
1297**应用代码**
1298
1299```typescript
1300
1301// file1.ts
1302
1303export class Test {
1304  value: string = '';
1305  foo(): void {
1306    globalThis.value = this.value;
1307  }
1308}
1309
1310// file2.ts
1311
1312globalThis.value;
1313
1314```
1315
1316**建议改法**
1317
1318```typescript
1319
1320// file1.ts
1321
1322import { GlobalContext } from '../GlobalContext'
1323
1324export class Test {
1325  value: string = '';
1326  foo(): void {
1327    GlobalContext.getContext().setObject('value', this.value);
1328  }
1329}
1330
1331// file2.ts
1332
1333import { GlobalContext } from '../GlobalContext'
1334
1335GlobalContext.getContext().getObject('value');
1336```
1337
1338## arkts-no-func-apply-bind-call
1339
1340### 使用标准库中接口
1341
1342**应用代码**
1343
1344```typescript
1345let arr: number[] = [1, 2, 3, 4];
1346let str = String.fromCharCode.apply(null, Array.from(arr));
1347```
1348
1349**建议改法**
1350
1351```typescript
1352let arr: number[] = [1, 2, 3, 4];
1353let str = String.fromCharCode(...Array.from(arr));
1354```
1355
1356### bind定义方法
1357
1358**应用代码**
1359
1360```typescript
1361class A {
1362  value: string = ''
1363  foo: Function = () => {}
1364}
1365
1366class Test {
1367  value: string = '1234'
1368  obj: A = {
1369    value: this.value,
1370    foo: this.foo.bind(this)
1371  }
1372
1373  foo() {
1374    console.log(this.value);
1375  }
1376}
1377```
1378
1379**建议改法1**
1380
1381```typescript
1382class A {
1383  value: string = ''
1384  foo: Function = () => {}
1385}
1386
1387class Test {
1388  value: string = '1234'
1389  obj: A = {
1390    value: this.value,
1391    foo: (): void => this.foo()
1392  }
1393
1394  foo() {
1395    console.log(this.value);
1396  }
1397}
1398```
1399
1400**建议改法2**
1401
1402```typescript
1403class A {
1404  value: string = ''
1405  foo: Function = () => {}
1406}
1407
1408class Test {
1409  value: string = '1234'
1410  foo: () => void = () => {
1411    console.log(this.value);
1412  }
1413  obj: A = {
1414    value: this.value,
1415    foo: this.foo
1416  }
1417}
1418```
1419
1420### 使用apply
1421
1422**应用代码**
1423
1424```typescript
1425class A {
1426  value: string;
1427  constructor (value: string) {
1428    this.value = value;
1429  }
1430
1431  foo() {
1432    console.log(this.value);
1433  }
1434}
1435
1436let a1 = new A('1');
1437let a2 = new A('2');
1438
1439a1.foo();
1440a1.foo.apply(a2);
1441```
1442
1443**建议改法**
1444
1445```typescript
1446class A {
1447  value: string;
1448  constructor (value: string) {
1449    this.value = value;
1450  }
1451
1452  foo() {
1453    this.fooApply(this);
1454  }
1455
1456  fooApply(a: A) {
1457    console.log(a.value);
1458  }
1459}
1460
1461let a1 = new A('1');
1462let a2 = new A('2');
1463
1464a1.foo();
1465a1.fooApply(a2);
1466```
1467
1468## arkts-limited-stdlib
1469
1470### `Object.fromEntries()`
1471
1472**应用代码**
1473
1474```typescript
1475let entries = new Map([
1476  ['foo', 123],
1477  ['bar', 456]
1478]);
1479
1480let obj = Object.fromEntries(entries);
1481```
1482
1483**建议改法**
1484
1485```typescript
1486let entries = new Map([
1487  ['foo', 123],
1488  ['bar', 456]
1489]);
1490
1491let obj: Record<string, Object> = {};
1492entries.forEach((value, key) => {
1493  if (key != undefined && key != null) {
1494    obj[key] = value;
1495  }
1496})
1497```
1498
1499### 使用`Number`的属性和方法
1500
1501ArkTS不允许使用全局对象的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt`
1502
1503可以使用`Number`的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt`
1504
1505**应用代码**
1506
1507```typescript
1508NaN;
1509isFinite(123);
1510parseInt('123');
1511```
1512
1513**建议改法**
1514
1515```typescript
1516Number.NaN;
1517Number.isFinite(123);
1518Number.parseInt('123');
1519```
1520
1521## arkts-strict-typing(StrictModeError)
1522
1523### strictPropertyInitialization
1524
1525**应用代码**
1526
1527```typescript
1528interface I {
1529  name:string
1530}
1531
1532class A {}
1533
1534class Test {
1535  a: number;
1536  b: string;
1537  c: boolean;
1538  d: I;
1539  e: A;
1540}
1541
1542```
1543
1544**建议改法**
1545
1546```typescript
1547interface I {
1548  name:string
1549}
1550
1551class A {}
1552
1553class Test {
1554  a: number;
1555  b: string;
1556  c: boolean;
1557  d: I = { name:'abc' };
1558  e: A | null = null;
1559  constructor(a:number, b:string, c:boolean) {
1560    this.a = a;
1561    this.b = b;
1562    this.c = c;
1563  }
1564}
1565
1566```
1567### Type `*** | null` is not assignable to type `***`
1568
1569**应用代码**
1570
1571```typescript
1572class A {
1573  bar() {}
1574}
1575function foo(n: number) {
1576  if (n === 0) {
1577    return null;
1578  }
1579  return new A();
1580}
1581function getNumber() {
1582  return 5;
1583}
1584let a:A = foo(getNumber());
1585a.bar();
1586```
1587
1588**建议改法**
1589
1590```typescript
1591class A {
1592  bar() {}
1593}
1594function foo(n: number) {
1595  if (n === 0) {
1596    return null;
1597  }
1598  return new A();
1599}
1600function getNumber() {
1601  return 5;
1602}
1603
1604let a: A | null = foo(getNumber());
1605a?.bar();
1606```
1607
1608### 严格属性初始化检查
1609
1610在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。
1611
1612**建议改法**
1613
16141.一般情况下,**建议按照业务逻辑**在声明时初始化属性,或者在构造函数中为属性赋值。如:
1615
1616```typescript
1617//code with error
1618class Test {
1619  value: number
1620  flag: boolean
1621}
1622
1623//方式一,在声明时初始化
1624class Test {
1625  value: number = 0
1626  flag: boolean = false
1627}
1628
1629//方式二,在构造函数中赋值
1630class Test {
1631  value: number
1632  flag: boolean
1633  constructor(value: number, flag: boolean) {
1634    this.value = value;
1635    this.flag = flag;
1636  }
1637}
1638```
1639
16402.对于对象类型(包括函数类型)`A`,如果不确定如何初始化,建议按照以下方式之一进行初始化
1641
1642​	方式(i)  `prop: A | null = null`
1643
1644​	方式(ii) `prop?: A`
1645
1646​	方式三(iii) `prop: A | undefined = undefined`
1647
1648- 从性能角度来说,`null`类型只用在编译期的类型检查中,对虚拟机的性能无影响。而`undefined | A`被视为联合类型,运行时可能有额外的开销。
1649- 从代码可读性、简洁性的角度来说,`prop?:A`是`prop: A | undefined = undefined`的语法糖,**推荐使用可选属性的写法**
1650
1651### 严格函数类型检查
1652
1653**应用代码**
1654
1655```typescript
1656function foo(fn: (value?: string) => void, value: string): void {}
1657
1658foo((value: string) => {}, ''); //error
1659```
1660
1661**建议改法**
1662
1663```typescript
1664function foo(fn: (value?: string) => void, value: string): void {}
1665
1666foo((value?: string) => {}, '');
1667```
1668
1669**原因**
1670
1671例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在`foo`的函数体中,一个`undefined`被传入`fn`(这是可以的,因为`fn`可以接受`undefined`),但是在代码第6行`foo`的调用点,传入的`(value: string) => { console.log(value.toUpperCase()) }`的函数实现中,始终将参数`value`当做string类型,允许其调用`toUpperCase`方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在`undefined`上无法找到属性的错误。
1672
1673```typescript
1674function foo(fn: (value?: string) => void, value: string): void {
1675  let v: string | undefined = undefined;
1676  fn(v);
1677}
1678
1679foo((value: string) => { console.log(value.toUpperCase()) }, ''); // Cannot read properties of undefined (reading 'toUpperCase')
1680```
1681
1682为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。
1683
1684### 严格空值检查
1685
1686**应用代码**
1687
1688```typescript
1689class Test {
1690  private value?: string
1691
1692  public printValue () {
1693    console.log(this.value.toLowerCase());
1694  }
1695}
1696
1697let t = new Test();
1698t.printValue();
1699```
1700
1701**建议改法**
1702
1703在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。
1704
1705```typescript
1706class Test {
1707  private value?: string
1708
1709  public printValue () {
1710    if (this.value) {
1711      console.log(this.value.toLowerCase());
1712    }
1713  }
1714}
1715
1716let t = new Test();
1717t.printValue();
1718```
1719
1720**原因**
1721
1722在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为`t`的属性`value`为`undefined`(这是因为`value?: string`是`value: string | undefined = undefined`的语法糖),在第11行调用`printValue`方法时,由于在该方法体内未对`this.value`的值进行空值检查,而直接按照`string`类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。
1723
1724### 函数返回类型不匹配
1725
1726**应用代码**
1727
1728```typescript
1729class Test {
1730  handleClick: (action: string, externInfo?: string) => void | null = null;
1731}
1732```
1733
1734**建议改法**
1735
1736在这种写法下,函数返回类型被解析为 `void | undefined`,需要添加括号用来区分union类型。
1737
1738```typescript
1739class Test {
1740  handleClick: ((action: string, externInfo?: string) => void) | null = null;
1741}
1742```
1743
1744### '***' is of type 'unknown'
1745
1746**应用代码**
1747
1748```typescript
1749try {
1750
1751} catch (error) {
1752  console.log(error.message);
1753}
1754```
1755
1756**建议改法**
1757
1758```typescript
1759import { BusinessError } from '@kit.BasicServicesKit'
1760
1761try {
1762
1763} catch (error) {
1764  console.log((error as BusinessError).message);
1765}
1766```
1767
1768### Type '*** | null' is not assignable to type '\*\*\*'
1769
1770**应用代码**
1771
1772```typescript
1773class A {
1774  value: number
1775  constructor(value: number) {
1776    this.value = value;
1777  }
1778}
1779
1780function foo(v: number): A | null {
1781  if (v > 0) {
1782    return new A(v);
1783  }
1784  return null;
1785}
1786
1787let a: A = foo();
1788```
1789
1790**建议改法1**
1791
1792修改变量`a`的类型:`let a: A | null = foo()`。
1793
1794```typescript
1795class A {
1796  value: number
1797  constructor(value: number) {
1798    this.value = value;
1799  }
1800}
1801
1802function foo(v: number): A | null {
1803  if (v > 0) {
1804    return new A(v);
1805  }
1806  return null;
1807}
1808
1809let a: A | null = foo(123);
1810
1811if (a != null) {
1812  // 非空分支
1813} else {
1814  // 处理null
1815}
1816```
1817
1818**建议改法2**
1819
1820如果可以断定此处调用`foo`一定返回非空值,可以使用非空断言`!`。
1821
1822```typescript
1823class A {
1824  value: number
1825  constructor(value: number) {
1826    this.value = value;
1827  }
1828}
1829
1830function foo(v: number): A | null {
1831  if (v > 0) {
1832    return new A(v);
1833  }
1834  return null;
1835}
1836
1837let a: A = foo(123)!;
1838```
1839
1840### Cannot invoke an object which possibly 'undefined'
1841
1842**应用代码**
1843
1844```typescript
1845interface A {
1846  foo?: () => void
1847}
1848
1849let a:A = { foo: () => {} };
1850a.foo();
1851```
1852
1853**建议改法1**
1854
1855```typescript
1856interface A {
1857  foo: () => void
1858}
1859let a: A = { foo: () => {} };
1860a.foo();
1861```
1862
1863**建议改法2**
1864
1865```typescript
1866interface A {
1867  foo?: () => void
1868}
1869
1870let a: A = { foo: () => {} };
1871if (a.foo) {
1872  a.foo();
1873}
1874```
1875
1876**原因**
1877
1878在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。
1879
1880### Variable '***' is used before being assigned
1881
1882**应用代码**
1883
1884```typescript
1885class Test {
1886  value: number = 0
1887}
1888
1889let a: Test
1890try {
1891  a = { value: 1};
1892} catch (e) {
1893  a.value;
1894}
1895a.value;
1896```
1897
1898**建议改法**
1899
1900```typescript
1901class Test {
1902  value: number = 0
1903}
1904
1905let a: Test | null = null;
1906try {
1907  a = { value:1 };
1908} catch (e) {
1909  if (a) {
1910    a.value;
1911  }
1912}
1913
1914if (a) {
1915  a.value;
1916}
1917```
1918
1919**原因**
1920
1921对于primitive types,可以根据业务逻辑赋值,例如0,'',false。
1922
1923对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。
1924
1925### Function lacks ending return statement and return type does not include 'undefined'.
1926
1927**应用代码**
1928
1929```typescript
1930function foo(a: number): number {
1931  if (a > 0) {
1932    return a;
1933  }
1934}
1935```
1936
1937**建议改法1**
1938
1939根据业务逻辑,在else分支中返回合适的数值
1940
1941**建议改法2**
1942
1943```typescript
1944function foo(a: number): number | undefined {
1945  if (a > 0) {
1946    return a;
1947  }
1948  return
1949}
1950```
1951
1952## arkts-strict-typing-required
1953
1954**应用代码**
1955
1956```typescript
1957// @ts-nocheck
1958var a: any = 123;
1959```
1960
1961**建议改法**
1962
1963```typescript
1964let a: number = 123;
1965```
1966
1967**原因**
1968
1969ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(`// @ts-nocheck`或者`// @ts-ignore`)删去,再根据报错信息修改其他代码。
1970
1971## Importing ArkTS files to JS and TS files is not allowed
1972
1973## arkts-no-tsdeps
1974
1975不允许.ts、.js文件`import`.ets文件源码。
1976
1977**建议改法**
1978
1979方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。
1980
1981方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。
1982
1983## arkts-no-special-imports
1984
1985**应用代码**
1986
1987```typescript
1988import type {A, B, C, D } from '***'
1989```
1990
1991
1992**建议改法**
1993
1994```typescript
1995import {A, B, C, D } from '***'
1996```
1997
1998## arkts-no-classes-as-obj
1999
2000### 使用class构造实例
2001
2002**应用代码**
2003
2004```typescript
2005class Controller {
2006  value: string = ''
2007  constructor(value: string) {
2008    this.value = value
2009  }
2010}
2011
2012interface ControllerConstructor {
2013  new (value: string): Controller;
2014}
2015
2016class Menu {
2017  controller: ControllerConstructor = Controller
2018  createController() {
2019    if (this.controller) {
2020      return new this.controller('abc');
2021    }
2022    return null;
2023  }
2024}
2025
2026let t = new Menu();
2027console.log(t.createController()!.value);
2028```
2029
2030**建议改法**
2031
2032```typescript
2033class Controller {
2034  value: string = ''
2035  constructor(value: string) {
2036    this.value = value
2037  }
2038}
2039
2040type ControllerConstructor = () => Controller;
2041
2042class Menu {
2043  controller: ControllerConstructor = () => { return new Controller('abc'); }
2044  createController() {
2045    if (this.controller) {
2046      return this.controller();
2047    }
2048    return null;
2049  }
2050}
2051
2052let t: Menu = new Menu();
2053console.log(t.createController()!.value);
2054```
2055
2056### 访问静态属性
2057
2058**应用代码**
2059
2060```typescript
2061class C1 {
2062  static value: string = 'abc'
2063}
2064
2065class C2 {
2066  static value: string = 'def'
2067}
2068
2069function getValue(obj: any) {
2070  return obj['value'];
2071}
2072
2073console.log(getValue(C1));
2074console.log(getValue(C2));
2075```
2076
2077**建议改法**
2078
2079```typescript
2080class C1 {
2081  static value: string = 'abc'
2082}
2083
2084class C2 {
2085  static value: string = 'def'
2086}
2087
2088function getC1Value(): string {
2089  return C1.value;
2090}
2091
2092function getC2Value(): string {
2093  return C2.value;
2094}
2095
2096console.log(getC1Value());
2097console.log(getC2Value());
2098```
2099
2100## arkts-no-side-effects-imports
2101
2102改用动态import
2103
2104**应用代码**
2105
2106```typescript
2107import 'module'
2108```
2109
2110**建议改法**
2111
2112```typescript
2113import('module')
2114```
2115
2116## arkts-no-func-props
2117
2118**应用代码**
2119
2120```typescript
2121function foo(value: number): void {
2122  console.log(value.toString());
2123}
2124
2125foo.add = (left: number, right: number) => {
2126  return left + right;
2127}
2128
2129foo.sub = (left: number, right: number) => {
2130  return left - right;
2131}
2132```
2133
2134**建议改法**
2135
2136```typescript
2137class Foo {
2138  static foo(value: number): void {
2139    console.log(value.toString());
2140  }
2141
2142  static add(left: number, right: number): number {
2143    return left + right;
2144  }
2145
2146  static sub(left: number, right: number): number {
2147    return left - right;
2148  }
2149}
2150```
2151
2152## arkts-limited-esobj
2153
2154**应用代码**
2155
2156```typescript
2157// lib.d.ts
2158declare function foo(): any;
2159
2160// main.ets
2161let e0: ESObject = foo();
2162
2163function f() {
2164  let e1 = foo();
2165  let e2: ESObject = 1;
2166  let e3: ESObject = {};
2167  let e4: ESObject = '';
2168}
2169```
2170
2171**建议改法**
2172
2173```typescript
2174// lib.d.ts
2175declare function foo(): any;
2176
2177// main.ets
2178interface I {}
2179
2180function f() {
2181  let e0: ESObject = foo();
2182  let e1: ESObject = foo();
2183  let e2: number = 1;
2184  let e3: I = {};
2185  let e4: string = '';
2186}
2187```
2188
2189## 拷贝
2190
2191### 浅拷贝
2192
2193**TypeScript**
2194
2195```typescript
2196function shallowCopy(obj: object): object {
2197  let newObj = {};
2198  Object.assign(newObj, obj);
2199  return newObj;
2200}
2201```
2202
2203**ArkTS**
2204
2205```typescript
2206function shallowCopy(obj: object): object {
2207  let newObj: Record<string, Object> = {};
2208  for (let key of Object.keys(obj)) {
2209    newObj[key] = obj[key];
2210  }
2211  return newObj;
2212}
2213```
2214
2215### 深拷贝
2216
2217**TypeScript**
2218
2219```typescript
2220function deepCopy(obj: object): object {
2221  let newObj = Array.isArray(obj) ? [] : {};
2222  for (let key in obj) {
2223    if (typeof obj[key] === 'object') {
2224      newObj[key] = deepCopy(obj[key]);
2225    } else {
2226      newObj[key] = obj[key];
2227    }
2228  }
2229  return newObj;
2230}
2231```
2232
2233**ArkTS**
2234
2235```typescript
2236function deepCopy(obj: object): object {
2237  let newObj: Record<string, Object> | Object[] = Array.isArray(obj) ? [] : {};
2238  for (let key of Object.keys(obj)) {
2239    if (typeof obj[key] === 'object') {
2240      newObj[key] = deepCopy(obj[key]);
2241    } else {
2242      newObj[key] = obj[key];
2243    }
2244  }
2245  return newObj;
2246}
2247```
2248
2249## 状态管理使用典型场景
2250
2251### Struct组件外使用状态变量
2252
2253由于struct和class不同,不建议把this作为参数传递到struct外部使用,避免引起实例引用无法释放的情况,导致内存泄露。建议将状态变量对象传递到struct外面使用,通过修改对象的属性,来触发UI刷新。
2254
2255**不推荐用法**
2256
2257```typescript
2258export class MyComponentController {
2259  item: MyComponent = null;
2260
2261  setItem(item: MyComponent) {
2262    this.item = item;
2263  }
2264
2265  changeText(value: string) {
2266    this.item.value = value;
2267  }
2268}
2269
2270@Component
2271export default struct MyComponent {
2272  public controller: MyComponentController = null;
2273  @State value: string = 'Hello World';
2274
2275  build() {
2276    Column() {
2277      Text(this.value)
2278        .fontSize(50)
2279    }
2280  }
2281
2282  aboutToAppear() {
2283    if (this.controller)
2284      this.controller.setItem(this); // 不建议把this作为参数传递到struct外部使用
2285  }
2286}
2287
2288@Entry
2289@Component
2290struct ObjThisOldPage {
2291  controller = new MyComponentController();
2292
2293  build() {
2294    Column() {
2295      MyComponent({ controller: this.controller })
2296      Button('change value').onClick(() => {
2297        this.controller.changeText('Text');
2298      })
2299    }
2300  }
2301}
2302```
2303
2304**推荐用法**
2305
2306```typescript
2307class CC {
2308  value: string = '1';
2309
2310  constructor(value: string) {
2311    this.value = value;
2312  }
2313}
2314
2315export class MyComponentController {
2316  item: CC = new CC('1');
2317
2318  setItem(item: CC) {
2319    this.item = item;
2320  }
2321
2322  changeText(value: string) {
2323    this.item.value = value;
2324  }
2325}
2326
2327@Component
2328export default struct MyComponent {
2329  public controller: MyComponentController | null = null;
2330  @State value: CC = new CC('Hello World');
2331
2332  build() {
2333    Column() {
2334      Text(`${this.value.value}`)
2335        .fontSize(50)
2336    }
2337  }
2338
2339  aboutToAppear() {
2340    if (this.controller)
2341      this.controller.setItem(this.value);
2342  }
2343}
2344
2345@Entry
2346@Component
2347struct StyleExample {
2348  controller: MyComponentController = new MyComponentController();
2349
2350  build() {
2351    Column() {
2352      MyComponent({ controller: this.controller })
2353      Button('change value').onClick(() => {
2354        this.controller.changeText('Text');
2355      })
2356    }
2357  }
2358}
2359```
2360
2361### Struct支持联合类型的方案
2362
2363下面这段代码有arkts-no-any-unknown的报错,由于struct不支持泛型,建议使用联合类型,实现自定义组件类似泛型的功能。
2364
2365**不推荐用法**
2366
2367```typescript
2368class Data {
2369  aa: number = 11;
2370}
2371
2372@Entry
2373@Component
2374struct DatauionOldPage {
2375  @State array: Data[] = [new Data(), new Data(), new Data()];
2376
2377  @Builder
2378  componentCloser(data: Data) {
2379    Text(data.aa + '').fontSize(50)
2380  }
2381
2382  build() {
2383    Row() {
2384      Column() {
2385        ForEachCom({ arrayList: this.array, closer: this.componentCloser })
2386      }
2387      .width('100%')
2388    }
2389    .height('100%')
2390  }
2391}
2392
2393@Component
2394export struct ForEachCom {
2395  arrayList: any[]; // struct不支持泛型,有arkts-no-any-unknown报错
2396  @BuilderParam closer: (data: any) => void = this.componentCloser; // struct不支持泛型,有arkts-no-any-unknown报错
2397
2398  @Builder
2399  componentCloser() {
2400  }
2401
2402  build() {
2403    Column() {
2404      ForEach(this.arrayList, (item: any) => { // struct不支持泛型,有arkts-no-any-unknown报错
2405        Row() {
2406          this.closer(item)
2407        }.width('100%').height(200).backgroundColor('#eee')
2408      })
2409    }
2410  }
2411}
2412```
2413
2414**推荐用法**
2415
2416```typescript
2417class Data {
2418  aa: number = 11;
2419}
2420
2421class Model {
2422  aa: string = '11';
2423}
2424
2425type UnionData = Data | Model;
2426
2427@Entry
2428@Component
2429struct DatauionPage {
2430  array: UnionData[] = [new Data(), new Data(), new Data()];
2431
2432  @Builder
2433  componentCloser(data: UnionData) {
2434    if (data instanceof Data) {
2435      Text(data.aa + '').fontSize(50)
2436    }
2437  }
2438
2439  build() {
2440    Row() {
2441      Column() {
2442        ForEachCom({ arrayList: this.array, closer: this.componentCloser })
2443      }
2444      .width('100%')
2445    }
2446    .height('100%')
2447  }
2448}
2449
2450@Component
2451export struct ForEachCom {
2452  arrayList: UnionData[] = [new Data(), new Data(), new Data()];
2453  @BuilderParam closer: (data: UnionData) => void = this.componentCloser;
2454
2455  @Builder
2456  componentCloser() {
2457  }
2458
2459  build() {
2460    Column() {
2461      ForEach(this.arrayList, (item: UnionData) => {
2462        Row() {
2463          this.closer(item)
2464        }.width('100%').height(200).backgroundColor('#eee')
2465      })
2466    }
2467  }
2468}
2469```