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```