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