• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
113  console.log(obj.value)
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: number = 0
159
160  constructor(value: number) {
161    this.value = value
162  }
163}
164
165type ControllerConstrucotr = {
166  new (value: number): Controller;
167}
168
169class Menu {
170  controller: ControllerConstrucotr = 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: number = 0
188
189  constructor(value: number) {
190    this.value = value
191  }
192}
193
194type ControllerConstrucotr = () => Controller;
195
196class Menu {
197  controller: ControllerConstrucotr = () => {
198    return new Controller(123)
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: number = 0
295
296  constructor(value: number) {
297    this.value = value
298  }
299}
300
301interface ControllerConstrucotr {
302  new (value: number): Controller;
303}
304
305class Menu {
306  controller: ControllerConstrucotr = Controller
307  createController() {
308    if (this.controller) {
309      return new this.controller(123)
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: number = 0
324
325  constructor(value: number) {
326    this.value = value
327  }
328}
329
330type ControllerConstrucotr = () => Controller;
331
332class Menu {
333  controller: ControllerConstrucotr = () => {
334    return new Controller(123)
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 myRouter from '@ohos.router';
357let params: Object = myRouter.getParams();
358let funNum: number = params['funNum'];
359let target: string = params['target'];
360```
361
362**建议改法**
363
364```typescript
365import myRouter from '@ohos.router';
366let params = myRouter.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 '@ohos.multimedia.image';
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
663import hilog from '@ohos.hilog'
664
665export default {
666  onCreate() {
667    hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
668  },
669  onDestroy() {
670    hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
671  }
672}
673```
674
675**建议改法**
676
677```typescript
678import hilog from '@ohos.hilog'
679
680class Test {
681  onCreate() {
682    hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
683  }
684  onDestroy() {
685    hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
686  }
687}
688
689export default new Test()
690```
691
692### 通过导入namespace获取类型
693
694**应用代码**
695
696```typescript
697import { BusinessError } from '@ohos.base';
698import bluetooth from '@ohos.bluetooth';
699let serverNumber = -1;
700function serverSocket(code: BusinessError, num: number) {
701  console.log('bluetooth error code: ' + code.code);
702  if (code.code == 0) {
703    console.log('bluetooth serverSocket Number: ' + num);
704    serverNumber = num;
705  }
706}
707
708let sppOption = { uuid: '', secure: false, type: 0 };
709bluetooth.sppListen('', sppOption, serverSocket);
710```
711
712**建议改法**
713
714```typescript
715import { BusinessError } from '@ohos.base';
716import bluetooth from '@ohos.bluetooth';
717let serverNumber = -1;
718function serverSocket(code: BusinessError, num: number) {
719  console.log('bluetooth error code: ' + code.code);
720  if (code.code == 0) {
721    console.log('bluetooth serverSocket Number: ' + num);
722    serverNumber = num;
723  }
724}
725
726let sppOption: bluetooth.SppOption = { uuid: '', secure: false, type: 0 };
727bluetooth.sppListen('', sppOption, serverSocket);
728```
729
730**原因**
731
732对象字面量缺少类型,根据`bluetooth.sppListen`分析可以得知,`sppOption`的类型来源于SDK,那么只需要将类型导入即可。
733注意到在`@ohos.bluetooth`中,`sppOption`是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。
734
735### object literal传参给Object类型
736
737**应用代码**
738
739```typescript
740function emit(event: string, ...args: Object[]): void {}
741
742emit('', {
743  'action': 11,
744  'outers': false
745})
746```
747
748**建议改法**
749
750```typescript
751function emit(event: string, ...args: Object[]): void {}
752
753let emitArg: Record<string, number | boolean> = {
754   'action': 11,
755   'outers': false
756}
757
758emit('', emitArg)
759```
760
761## arkts-no-obj-literals-as-types
762
763**应用代码**
764
765```typescript
766type Person = { name: string, age: number }
767```
768
769**建议改法**
770
771```typescript
772interface Person {
773  name: string,
774  age: number
775}
776```
777
778## arkts-no-noninferrable-arr-literals
779
780**应用代码**
781
782```typescript
783let permissionList = [
784  { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
785  { name: '麦克风', value: '用于反馈问题单时增加语音' },
786  { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
787]
788```
789
790**建议改法**
791
792为对象字面量声明类型
793
794```typescript
795class PermissionItem {
796  name?: string
797  value?: string
798}
799
800let permissionList: PermissionItem[] = [
801  { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
802  { name: '麦克风', value: '用于反馈问题单时增加语音' },
803  { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
804]
805```
806
807## arkts-no-method-reassignment
808
809**应用代码**
810
811```typescript
812class C {
813  add(left: number, right: number): number {
814    return left + right;
815  }
816}
817
818function sub(left: number, right: number): number {
819  return left - right;
820}
821
822let c1 = new C()
823c1.add = sub
824```
825
826**建议改法**
827
828```typescript
829class C {
830  add: (left: number, right: number) => number =
831    (left: number, right: number) => {
832      return left + right;
833    }
834}
835
836function sub(left: number, right: number): number {
837  return left - right;
838}
839
840let c1 = new C()
841c1.add = sub
842```
843
844## arkts-no-polymorphic-unops
845
846**应用代码**
847
848```typescript
849let a = +'5'
850let b = -'5'
851let c = ~'5'
852let d = +'string'
853```
854
855**建议改法**
856
857```typescript
858let a = Number.parseInt('5')
859let b = -Number.parseInt('5')
860let c = ~Number.parseInt('5')
861let d = new Number('string')
862```
863
864## arkts-no-type-query
865
866**应用代码**
867
868```typescript
869// module1.ts
870class C {
871  value: number = 0
872}
873
874export let c = new C()
875
876// module2.ts
877import { c } from './module1'
878let t: typeof c = { value: 123 }
879```
880
881**建议改法**
882
883```typescript
884// module1.ts
885class C {
886  value: number = 0
887}
888
889export { C }
890
891// module2.ts
892import { C } from './module1'
893let t: C = { value: 123 }
894```
895
896## arkts-no-in
897
898**应用代码**
899
900```typescript
901let arr = [10, 20, 30, 40]
902let isIn = 5 in arr
903```
904
905**建议改法**
906
907```typescript
908let arr = [10, 20, 30, 40]
909let isIn = 5 < arr.length
910```
911
912## arkts-no-destruct-assignment
913
914**应用代码**
915
916```typescript
917let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
918for (let [key, value] of map) {
919  console.log(key)
920  console.log(value)
921}
922```
923
924**建议改法**
925
926使用数组
927
928```typescript
929let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
930for (let arr of map) {
931  let key = arr[0]
932  let value = arr[1]
933  console.log(key)
934  console.log(value)
935}
936```
937
938## arkts-no-types-in-catch
939
940**应用代码**
941
942```typescript
943import { BusinessError } from '@ohos.base';
944
945try {
946  // ...
947} catch (e: BusinessError) {
948  logger.error(e.code, e.message);
949}
950```
951
952**建议改法**
953
954```typescript
955import { BusinessError } from '@ohos.base';
956
957try {
958  // ...
959} catch (error) {
960  let e: BusinessError = error as BusinessError;
961  logger.error(e.code, e.message);
962}
963```
964
965## arkts-no-for-in
966
967**应用代码**
968
969```typescript
970interface Person {
971  [name: string]: string
972}
973let p: Person = {
974  name: 'tom',
975  age: '18'
976};
977
978for (let t in p) {
979  console.log(p[t])
980}
981```
982
983**建议改法**
984
985```typescript
986let p: Record<string, string> = {
987  'name': 'tom',
988  'age': '18'
989};
990
991for (let ele of Object.entries(p)) {
992  console.log(ele[1])
993}
994```
995
996## arkts-no-mapped-types
997
998**应用代码**
999
1000```typescript
1001class C {
1002  a: number = 0
1003  b: number = 0
1004  c: number = 0
1005}
1006type OptionsFlags = {
1007  [Property in keyof C]: string
1008}
1009```
1010
1011**建议改法**
1012
1013```typescript
1014class C {
1015  a: number = 0
1016  b: number = 0
1017  c: number = 0
1018}
1019
1020type OptionsFlags = Record<keyof C, string>
1021```
1022
1023## arkts-limited-throw
1024
1025**应用代码**
1026
1027```typescript
1028import { BusinessError } from '@ohos.base';
1029
1030function ThrowError(error: BusinessError) {
1031  throw error
1032}
1033```
1034
1035**建议改法**
1036
1037```typescript
1038import { BusinessError } from '@ohos.base';
1039
1040function ThrowError(error: BusinessError) {
1041  throw error as Error
1042}
1043```
1044
1045**原因**
1046
1047`throw`语句中值的类型必须为`Error`或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用`as`将类型转换为`Error`。
1048
1049## arkts-no-standalone-this
1050
1051### 函数内使用this
1052
1053**应用代码**
1054
1055```typescript
1056function foo() {
1057  console.log(this.value)
1058}
1059
1060let obj = { value: 123 }
1061foo.apply(obj)
1062```
1063
1064**建议改法1**
1065
1066使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制
1067
1068```typescript
1069class Test {
1070  value: number = 0
1071  constructor (value: number) {
1072    this.value = value
1073  }
1074
1075  foo() {
1076    console.log(this.value)
1077  }
1078}
1079
1080let obj: Test = new Test(123)
1081obj.foo()
1082```
1083
1084**建议改法2**
1085
1086将this作为参数传入
1087
1088```typescript
1089function foo(obj: Test) {
1090  console.log(obj.value)
1091}
1092
1093class Test {
1094  value: number = 0
1095}
1096
1097let obj: Test = { value: 123 }
1098foo(obj)
1099```
1100
1101**建议改法3**
1102
1103将属性作为参数传入
1104```typescript
1105function foo(value: number) {
1106  console.log(value)
1107}
1108
1109class Test {
1110  value: number = 0
1111}
1112
1113let obj: Test = { value: 123 }
1114foo(obj.value)
1115```
1116
1117### class的静态方法内使用this
1118
1119**应用代码**
1120
1121```typescript
1122class Test {
1123  static value: number = 123
1124  static foo(): number {
1125    return this.value
1126  }
1127}
1128```
1129
1130**建议改法**
1131
1132```typescript
1133class Test {
1134  static value: number = 123
1135  static foo(): number {
1136    return Test.value
1137  }
1138}
1139```
1140
1141## arkts-no-spread
1142
1143**应用代码**
1144
1145```typescript
1146import notification from '@ohos.notificationManager';
1147
1148function buildNotifyLongRequest(): notification.NotificationRequest {
1149  // ...
1150}
1151
1152let notificationRequest: notification.NotificationRequest = {
1153  ...buildNotifyLongRequest(),
1154  deliveryTime: new Date().getTime()
1155}
1156```
1157
1158**建议改法**
1159
1160```typescript
1161import notification from '@ohos.notificationManager';
1162
1163function buildNotifyLongRequest():notification.NotificationRequest {
1164    // ...
1165}
1166
1167let notificationRequest: notification.NotificationRequest = buildNotifyLongRequest();
1168notificationRequest.deliveryTime = new Date().getTime();
1169```
1170
1171**原因**
1172
1173ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。
1174
1175## arkts-no-ctor-signatures-funcs
1176
1177在class内声明属性,而不是在构造函数上。
1178
1179**应用代码**
1180
1181```typescript
1182class Controller {
1183  value: number = 0
1184  constructor(value: number) {
1185    this.value = value
1186  }
1187}
1188
1189type ControllerConstrucotr = new (value: number) => Controller;
1190
1191class Menu {
1192  controller: ControllerConstrucotr = Controller
1193  createController() {
1194    if (this.controller) {
1195      return new this.controller(123)
1196    }
1197    return null;
1198  }
1199}
1200
1201let t = new Menu()
1202console.log(t.createController()!.value)
1203```
1204
1205**建议改法**
1206
1207```typescript
1208class Controller {
1209  value: number = 0
1210  constructor(value: number) {
1211    this.value = value
1212  }
1213}
1214
1215type ControllerConstrucotr = () => Controller;
1216
1217class Menu {
1218  controller: ControllerConstrucotr = () => { return new Controller(123) }
1219  createController() {
1220    if (this.controller) {
1221      return this.controller()
1222    }
1223    return null;
1224  }
1225}
1226
1227let t: Menu = new Menu()
1228console.log(t.createController()!.value)
1229```
1230
1231## arkts-no-globalthis
1232
1233由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。
1234
12351. 建议按照业务逻辑根据`import/export`语法实现数据在不同模块的传递。
1236
12372. 必要情况下,可以通过构造的**单例对象**来实现全局对象的功能。(**说明:**不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。)
1238
1239**构造单例对象**
1240
1241```typescript
1242// 构造单例对象
1243export class GlobalContext {
1244  private constructor() {}
1245  private static instance: GlobalContext;
1246  private _objects = new Map<string, Object>();
1247
1248  public static getContext(): GlobalContext {
1249    if (!GlobalContext.instance) {
1250      GlobalContext.instance = new GlobalContext();
1251    }
1252    return GlobalContext.instance;
1253  }
1254
1255  getObject(value: string): Object | undefined {
1256    return this._objects.get(value);
1257  }
1258
1259  setObject(key: string, objectClass: Object): void {
1260    this._objects.set(key, objectClass);
1261  }
1262}
1263
1264```
1265
1266**应用代码**
1267
1268```typescript
1269
1270// file1.ts
1271
1272export class Test {
1273  value: string = '';
1274  foo(): void {
1275    globalThis.value = this.value;
1276  }
1277}
1278
1279// file2.ts
1280
1281print(globalThis.value)
1282
1283```
1284
1285**建议改法**
1286
1287```typescript
1288
1289// file1.ts
1290
1291import { GlobalContext } from '../GlobalContext'
1292
1293export class Test {
1294  value: string = '';
1295  foo(): void {
1296    GlobalContext.getContext().setObject('value', this.value);
1297  }
1298}
1299
1300// file2.ts
1301
1302import { GlobalContext } from '../GlobalContext'
1303
1304console.log(GlobalContext.getContext().getObject('value'));
1305```
1306
1307## arkts-no-func-apply-bind-call
1308
1309### 使用标准库中接口
1310
1311**应用代码**
1312
1313```typescript
1314let arr: number[] = [1, 2, 3, 4]
1315let str = String.fromCharCode.apply(null, Array.from(arr))
1316```
1317
1318**建议改法**
1319
1320```typescript
1321let arr: number[] = [1, 2, 3, 4]
1322let str = String.fromCharCode(...Array.from(arr))
1323```
1324
1325### bind定义方法
1326
1327**应用代码**
1328
1329```typescript
1330class A {
1331  value: number = 0
1332  foo: Function = () => {}
1333}
1334
1335class Test {
1336  value: number = 1234
1337  obj: A = {
1338    value: this.value,
1339    foo: this.foo.bind(this)
1340  }
1341
1342  foo() {
1343    console.log(this.value)
1344  }
1345}
1346```
1347
1348**建议改法1**
1349
1350```typescript
1351class A {
1352  value: number = 0
1353  foo: Function = () => {}
1354}
1355
1356class Test {
1357  value: number = 1234
1358  obj: A = {
1359    value: this.value,
1360    foo: (): void => this.foo()
1361  }
1362
1363  foo() {
1364    console.log(this.value)
1365  }
1366}
1367```
1368
1369**建议改法2**
1370
1371```typescript
1372class A {
1373  value: number = 0
1374  foo: Function = () => {}
1375}
1376
1377class Test {
1378  value: number = 1234
1379  foo: () => void = () => {
1380    console.log(this.value)
1381  }
1382  obj: A = {
1383    value: this.value,
1384    foo: this.foo
1385  }
1386}
1387```
1388
1389### 使用apply
1390
1391**应用代码**
1392
1393```typescript
1394class A {
1395  value:number;
1396  constructor (value:number) {
1397    this.value = value
1398  }
1399
1400  foo() {
1401    console.log(this.value)
1402  }
1403}
1404
1405let a1 = new A(1)
1406let a2 = new A(2)
1407
1408a1.foo()
1409a1.foo.apply(a2)
1410```
1411
1412**建议改法**
1413
1414```typescript
1415class A {
1416  value:number;
1417  constructor (value:number) {
1418    this.value = value
1419  }
1420
1421  foo() {
1422    this.fooApply(this)
1423  }
1424
1425  fooApply(a: A) {
1426    console.log(a.value)
1427  }
1428}
1429
1430let a1 = new A(1)
1431let a2 = new A(2)
1432
1433a1.foo()
1434a1.fooApply(a2)
1435```
1436
1437## arkts-limited-stdlib
1438
1439ArkTS不允许使用全局对象的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt`
1440
1441可以使用`Number`的属性和方法: `Infinity, NaN, isFinite, isNaN, parseFloat, parseInt`
1442
1443**应用代码**
1444
1445```typescript
1446console.log(NaN)
1447console.log(isFinite(123))
1448console.log(parseInt('123'))
1449```
1450
1451**建议改法**
1452
1453```typescript
1454console.log(Number.NaN)
1455console.log(Number.isFinite(123))
1456console.log(Number.parseInt('123'))
1457```
1458
1459## arkts-strict-typing(StrictModeError)
1460
1461### strictPropertyInitialization
1462
1463**应用代码**
1464
1465```typescript
1466interface I {
1467  name:string
1468}
1469
1470class A {}
1471
1472class Test {
1473  a: number;
1474  b: string;
1475  c: boolean;
1476  d: I;
1477  e: A;
1478}
1479
1480```
1481
1482**建议改法**
1483
1484```typescript
1485interface I {
1486  name:string
1487}
1488
1489class A {}
1490
1491class Test {
1492  a: number;
1493  b: string;
1494  c: boolean;
1495  d: I = { name:'abc' };
1496  e: A | null = null;
1497  constructor(a:number, b:string, c:boolean) {
1498    this.a = a;
1499    this.b = b;
1500    this.c = c;
1501  }
1502}
1503
1504```
1505### Type `*** | null` is not assignable to type `***`
1506
1507**应用代码**
1508
1509```typescript
1510class A {
1511  bar() {}
1512}
1513function foo(n: number) {
1514  if (n === 0) {
1515    return null;
1516  }
1517  return new A();
1518}
1519function getNumber() {
1520  return 5;
1521}
1522let a:A = foo(getNumber());
1523a.bar();
1524```
1525
1526**建议改法**
1527
1528```typescript
1529class A {
1530  bar() {}
1531}
1532function foo(n: number) {
1533  if (n === 0) {
1534    return null;
1535  }
1536  return new A();
1537}
1538function getNumber() {
1539  return 5;
1540}
1541
1542let a: A | null = foo(getNumber());
1543a?.bar();
1544```
1545
1546### 严格属性初始化检查
1547
1548在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。
1549
1550**建议改法**
1551
15521.一般情况下,**建议按照业务逻辑**在声明时初始化属性,或者在构造函数中为属性赋值。如:
1553
1554```typescript
1555//code with error
1556class Test {
1557  value: nameber
1558  flag: boolean
1559}
1560
1561//方式一,在声明时初始化
1562class Test {
1563  value: number = 0
1564  flag: boolean = false
1565}
1566
1567//方式二,在构造函数中赋值
1568class Test {
1569  value: number
1570  flag: boolean
1571  constructor(value: number, flag: boolean) {
1572    this.value = value
1573    this.flag = flag
1574  }
1575}
1576```
1577
15782.对于对象类型(包括函数类型)`A`,如果不确定如何初始化,建议按照以下方式之一进行初始化
1579
1580​	方式(i)  `prop: A | null = null`
1581
1582​	方式(ii) `prop?: A`
1583
1584​	方式三(iii) `prop: A | undefined = undefined`
1585
1586- 从性能角度来说,`null`类型只用在编译期的类型检查中,对虚拟机的性能无影响。而`undefined | A`被视为联合类型,运行时可能有额外的开销。
1587- 从代码可读性、简洁性的角度来说,`prop?:A`是`prop: A | undefined = undefined`的语法糖,**推荐使用可选属性的写法**
1588
1589### 严格函数类型检查
1590
1591**应用代码**
1592
1593```typescript
1594function foo(fn: (value?: string) => void, value: string): void {}
1595
1596foo((value: string) => {}, '') //error
1597```
1598
1599**建议改法**
1600
1601```typescript
1602function foo(fn: (value?: string) => void, value: string): void {}
1603
1604foo((value?: string) => {}, '')
1605```
1606
1607**原因**
1608
1609例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在`foo`的函数体中,一个`undefined`被传入`fn`(这是可以的,因为`fn`可以接受`undefined`),但是在代码第6行`foo`的调用点,传入的`(value: string) => { console.log(value.toUpperCase()) }`的函数实现中,始终将参数`value`当做string类型,允许其调用`toUpperCase`方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在`undefined`上无法找到属性的错误。
1610
1611```typescript
1612function foo(fn: (value?: string) => void, value: string): void {
1613  let v: string | undefined = undefined
1614  fn(v)
1615}
1616
1617foo((value: string) => { console.log(value.toUpperCase()) }, '') // Cannot read properties of undefined (reading 'toUpperCase')
1618```
1619
1620为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。
1621
1622### 严格空值检查
1623
1624**应用代码**
1625
1626```typescript
1627class Test {
1628  private value?: string
1629
1630  public printValue () {
1631    console.log(this.value.toLowerCase())
1632  }
1633}
1634
1635let t = new Test()
1636t.printValue()
1637```
1638
1639**建议改法**
1640
1641在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。
1642
1643```typescript
1644class Test {
1645  private value?: string
1646
1647  public printValue () {
1648    if (this.value) {
1649      console.log(this.value.toLowerCase())
1650    }
1651  }
1652}
1653
1654let t = new Test()
1655t.printValue()
1656```
1657
1658**原因**
1659
1660在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为`t`的属性`value`为`undefined`(这是因为`value?: string`是`value : string | undefined = undefined`的语法糖),在第11行调用`printValue`方法时,由于在该方法体内未对`this.value`的值进行空值检查,而直接按照`string`类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。
1661
1662### 函数返回类型不匹配
1663
1664**应用代码**
1665
1666```typescript
1667class Test {
1668  handleClick: (action: string, externInfo?: DiaExternInfo) => void | null = null;
1669}
1670```
1671
1672**建议改法**
1673
1674在这种写法下,函数返回类型被解析为 `void | undefined`,需要添加括号用来区分union类型。
1675
1676```typescript
1677class Test {
1678  handleClick: ((action: string, externInfo?: DialogExternInfo) => void) | null = null;
1679}
1680```
1681
1682### '***' is of type 'unknown'
1683
1684**应用代码**
1685
1686```typescript
1687try {
1688
1689} catch (error) {
1690  console.log(error.message)
1691}
1692```
1693
1694**建议改法**
1695
1696```typescript
1697import { BusinessError } from '@ohos.base'
1698
1699try {
1700
1701} catch (error) {
1702  console.log((error as BusinessError).message)
1703}
1704```
1705
1706### Type '*** | null' is not assignable to type '\*\*\*'. Type 'null' is not assignable to type '\*\*\*'
1707
1708**应用代码**
1709
1710```typescript
1711class A {
1712  value: number
1713  constructor(value: number) {
1714    this.value = value
1715  }
1716}
1717
1718function foo(v: number): A | null {
1719  if (v > 0) {
1720    return new A(v)
1721  }
1722  return null
1723}
1724
1725let a: A = foo()
1726```
1727
1728**建议改法1**
1729
1730修改变量`a`的类型:`let a: A | null = foo()`。
1731
1732```typescript
1733class A {
1734  value: number
1735  constructor(value: number) {
1736    this.value = value
1737  }
1738}
1739
1740function foo(v: number): A | null {
1741  if (v > 0) {
1742    return new A(v)
1743  }
1744  return null
1745}
1746
1747let a: A | null = foo(123)
1748
1749if (a != null) {
1750  // 非空分支
1751} else {
1752  // 处理null
1753}
1754```
1755
1756**建议改法2**
1757
1758如果可以断定此处调用`foo`一定返回非空值,可以使用非空断言`!`。
1759
1760```typescript
1761class A {
1762  value: number
1763  constructor(value: number) {
1764    this.value = value
1765  }
1766}
1767
1768function foo(v: number): A | null {
1769  if (v > 0) {
1770    return new A(v)
1771  }
1772  return null
1773}
1774
1775let a: A = foo(123)!
1776```
1777
1778### Cannot invoke an object which possibly 'undefined'
1779
1780**应用代码**
1781
1782```typescript
1783interface A {
1784  foo?: () => void
1785}
1786
1787let a:A = { foo: () => {} }
1788a.foo()
1789```
1790
1791**建议改法1**
1792
1793```typescript
1794interface A {
1795  foo: () => void
1796}
1797let a: A = { foo: () => {} }
1798a.foo()
1799```
1800
1801**建议改法2**
1802
1803```typescript
1804interface A {
1805  foo?: () => void
1806}
1807
1808let a: A = { foo: () => {} }
1809if (a.foo) {
1810  a.foo()
1811}
1812```
1813
1814**原因**
1815
1816在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。
1817
1818### Variable '***' is used before being assigned
1819
1820**应用代码**
1821
1822```typescript
1823class Test {
1824  value: number = 0
1825}
1826
1827let a: Test
1828try {
1829  a = { value: 1}
1830} catch (e) {
1831  a.value
1832}
1833a.value
1834```
1835
1836**建议改法**
1837
1838```typescript
1839class Test {
1840  value: number = 0
1841}
1842
1843let a: Test | null = null
1844try {
1845  a = { value:1 }
1846} catch (e) {
1847  if (a) {
1848    a.value
1849  }
1850}
1851
1852if (a) {
1853  a.value
1854}
1855```
1856
1857**原因**
1858
1859对于primitive types,可以根据业务逻辑赋值,例如0,'',false。
1860
1861对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。
1862
1863### Function lacks ending return statement and return type does not include 'undefined'.
1864
1865**应用代码**
1866
1867```typescript
1868function foo(a: number): number {
1869  if (a > 0) {
1870    return a
1871  }
1872}
1873```
1874
1875**建议改法1**
1876
1877根据业务逻辑,在else分支中返回合适的数值
1878
1879**建议改法2**
1880
1881```typescript
1882function foo(a: number): number | undefined {
1883  if (a > 0) {
1884    return a
1885  }
1886  return
1887}
1888```
1889
1890## arkts-strict-typing-required
1891
1892**应用代码**
1893
1894```typescript
1895// @ts-nocheck
1896var a: any = 123
1897```
1898
1899**建议改法**
1900
1901```typescript
1902let a: number = 123
1903```
1904
1905**原因**
1906
1907ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(`// @ts-nocheck`或者`// @ts-ignore`)删去,再根据报错信息修改其他代码。
1908
1909## Importing ArkTS files to JS and TS files is not allowed
1910
1911## arkts-no-tsdeps
1912
1913不允许.ts、.js文件`import`.ets文件源码。
1914
1915**建议改法**
1916
1917方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。
1918
1919方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。
1920
1921## arkts-no-special-imports
1922
1923**应用代码**
1924
1925```typescript
1926import type {A, B, C, D } from '***'
1927```
1928
1929
1930**建议改法**
1931
1932```typescript
1933import {A, B, C, D } from '***'
1934```
1935
1936## arkts-no-classes-as-obj
1937
1938### 使用class构造实例
1939
1940**应用代码**
1941
1942```typescript
1943class Controller {
1944  value: number = 0
1945  constructor(value: number) {
1946    this.value = value
1947  }
1948}
1949
1950interface ControllerConstrucotr {
1951  new (value: number): Controller;
1952}
1953
1954class Menu {
1955  controller: ControllerConstrucotr = Controller
1956  createController() {
1957    if (this.controller) {
1958      return new this.controller(123)
1959    }
1960    return null;
1961  }
1962}
1963
1964let t = new Menu()
1965console.log(t.createController()!.value)
1966```
1967
1968**建议改法**
1969
1970```typescript
1971class Controller {
1972  value: number = 0
1973  constructor(value: number) {
1974    this.value = value
1975  }
1976}
1977
1978type ControllerConstrucotr = () => Controller;
1979
1980class Menu {
1981  controller: ControllerConstrucotr = () => { return new Controller(123) }
1982  createController() {
1983    if (this.controller) {
1984      return this.controller()
1985    }
1986    return null;
1987  }
1988}
1989
1990let t: Menu = new Menu()
1991console.log(t.createController()!.value)
1992```
1993
1994### 访问静态属性
1995
1996**应用代码**
1997
1998```typescript
1999class C1 {
2000  static value: string = 'abc'
2001}
2002
2003class C2 {
2004  static value: string = 'def'
2005}
2006
2007function getValue(obj: any) {
2008  return obj['value']
2009}
2010
2011console.log(getValue(C1))
2012console.log(getValue(C2))
2013```
2014
2015**建议改法**
2016
2017```typescript
2018class C1 {
2019  static value: string = 'abc'
2020}
2021
2022class C2 {
2023  static value: string = 'def'
2024}
2025
2026function getC1Value(): string {
2027  return C1.value;
2028}
2029
2030function getC2Value(): string {
2031  return C2.value;
2032}
2033
2034console.log(getC1Value())
2035console.log(getC2Value())
2036```
2037
2038## arkts-no-side-effects-imports
2039
2040改用动态import
2041
2042**应用代码**
2043
2044```typescript
2045import 'module'
2046```
2047
2048**建议改法**
2049
2050```typescript
2051import('module')
2052```
2053
2054## arkts-no-func-props
2055
2056**应用代码**
2057
2058```typescript
2059function foo(value: number): void {
2060  console.log(value.toString())
2061}
2062
2063foo.add = (left: number, right: number) => {
2064  return left + right
2065}
2066
2067foo.sub = (left: number, right: number) => {
2068  return left - right
2069}
2070```
2071
2072**建议改法**
2073
2074```typescript
2075class Foo {
2076  static foo(value: number): void {
2077    console.log(value.toString())
2078  }
2079
2080  static add(left: number, right: number): number {
2081    return left + right
2082  }
2083
2084  static sub(left: number, right: number): number {
2085    return left - right
2086  }
2087}
2088```
2089