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