1# ArkTS Coding Style Guide 2 3<!--Kit: ArkTS--> 4<!--Subsystem: ArkCompiler--> 5<!--Owner: @yyytiancai--> 6<!--Designer: @qyhuo32--> 7<!--Tester: @kirl75; @zsw_zhushiwei--> 8<!--Adviser: @zhang_yixin13--> 9 10## Purpose 11 12Based on the language characteristics of ArkTS, as well as industry standards and practices, this guide provides coding guide for improving code standardization, security, and performance. 13 14This guide is applicable when you use ArkTS for coding during application development. 15 16## Source 17 18ArkTS further enhances static check and analysis while maintaining the basic syntax style of TypeScript. Some rules in this topic are selected from the [TypeScript and JavaScript Coding Style Guide](https://gitcode.com/openharmony/docs/blob/master/en/contribute/OpenHarmony-Application-Typescript-JavaScript-coding-guide.md), providing standards for ArkTS-specific syntax to improve code readability and execution performance. 19 20## Document Structure 21 22### Coding Style 23 24Style for the naming and format. 25 26### Programming Practices 27 28Practices for declaration, initialization, data types, operations and expressions, and exceptions. 29 30Guidelines in *TypeScript and JavaScript Coding Style Guide* that are involved in the ArkTS language are extracted, and new guidelines are added for ArkTS-specific syntax. 31 32## Terms 33 34| Term | Acronym/Abbreviation | Description| 35| ---- | ---- | ----| 36| ArkTS | N/A| ArkTS programming language| 37| TypeScript | TS | TypeScript programming language| 38| JavaScript | JS | JavaScript programming language| 39| ESObject | N/A| JS/TS object in ArkTS cross-language calls| 40 41## Principle 42 43Guidelines are categorized as follows: 44 45**Rule**: a convention that must be complied with. All contents in this document are for ArkTS. 46 47**Recommendation**: a convention that must be considered. 48 49## Naming 50 51### Properly Name Identifiers to Make Them Easy to Read 52 53**[Description]** 54 55A well-named identifier meets the following requirements: 56 - Clearly express the intent. Do not use single letters or non-standard abbreviations. 57 - Use correct English words in line with the English grammar. Do not use Pinyin. 58 - Ensure that the statement is clear and unambiguous. 59 60### Use UpperCamelCase for Class Names, Enum Names, and Namespace Names 61 62**[Category]** Recommendation 63 64**[Description]** 65 66Classes are named in upper camel case. 67Class names are usually nouns or noun phrases, for example, Person, Student, and Worker. Avoid verbs and ambiguous words like Data and Info in class names. 68 69**[Correct Example]** 70``` 71// Class name 72class User { 73 username: string 74 75 constructor(username: string) { 76 this.username = username; 77 } 78 79 sayHi() { 80 console.log('hi' + this.username); 81 } 82} 83 84// Enum name 85enum UserType { 86 TEACHER = 0, 87 STUDENT = 1 88}; 89 90// Namespace name 91namespace Base64Utils { 92 function encrypt() { 93 // todo encrypt 94 } 95 96 function decrypt() { 97 // todo decrypt 98 } 99}; 100``` 101 102### Use lowerCamelCase for Variable Names, Method Names, and Parameter Names 103 104**[Category]** Recommendation 105 106**[Description]** 107 108A function is usually named as a verb or verb phrase in lower camel case. Examples are as follows: 1091. load + attributeName() 1102. put + attributeName() 1113. is + BooleanAttributeName() 1124. has + noun/adjective() 1135. verb() 1146. verb + object() 115A variable name is usually a noun or noun phrase in lower camel case. 116 117**[Correct Example]** 118``` 119let msg = 'Hello world'; 120 121function sendMsg(msg: string) { 122 // todo send message 123} 124 125let userName = 'Zhangsan'; 126 127function findUser(userName: string) { 128 // todo find user by user name 129} 130``` 131 132### Use Uppercase Letters for Constant Names and Enum Value Names and Separate Words by Underscores 133 134**[Category]** Recommendation 135 136**[Description]** 137 138A constant name must consist of uppercase letters separated by underscores (_). A constant name should express complete semantics whenever possible. 139 140**[Correct Example]** 141 142``` 143const MAX_USER_SIZE = 10000; 144 145enum UserType { 146 TEACHER = 0, 147 STUDENT = 1 148}; 149``` 150 151### Do Not Use Negative Boolean Variable Names 152 153**[Category]** Recommendation 154 155**[Description]** 156 157It is recommended that a local variable of the Boolean type be prefixed with a meaningless expression, for example, is, has, can, or should. It is confusing when a logical NOT operator is used in a double negative phrase, for example, !isNotError. Therefore, avoid defining negative Boolean variable names. 158 159**[Incorrect Example]** 160 161``` 162let isNoError = true; 163let isNotFound = false; 164 165function empty() {} 166function next() {} 167``` 168 169**[Correct Example]** 170 171``` 172let isError = false; 173let isFound = true; 174 175function isEmpty() {} 176function hasNext() {} 177``` 178 179## Format 180 181### Use Spaces for Indentation 182 183**[Category]** Recommendation 184 185**[Description]** 186 187Use spaces only to indent. 188 189Preferentially use two-space indentation in most scenarios. Use four spaces in line break scenarios. 190Do not use the Tab key to indent. Currently, almost all IDEs and code editors support automatic conversion of a Tab input to two spaces. The code editors should be configured to use spaces for indentation. 191 192**[Correct Example]** 193 194``` 195class DataSource { 196 id: number = 0 197 title: string = '' 198 content: string = '' 199} 200 201const dataSource: DataSource[] = [ 202 { 203 id: 1, 204 title: 'Title 1', 205 content: 'Content 1' 206 }, 207 { 208 id: 2, 209 title: 'Title 2', 210 content: 'Content 2' 211 } 212 213]; 214 215function test(dataSource: DataSource[]) { 216 if (!dataSource.length) { 217 return; 218 } 219 220 for (let data of dataSource) { 221 if (!data || !data.id || !data.title || !data.content) { 222 continue; 223 } 224 // some code 225 } 226 227 // some code 228} 229``` 230 231### Use No More Than 120 Characters in Each Line 232 233**[Category]** Recommendation 234 235**[Description]** 236 237The code line width should not be too long. Otherwise, it is difficult to read. 238 239The line width requirement encourages you to shorten function and variable names, reduce nesting, and write concise comments to improve code readability. 240It is recommended that each line contain no more than 120 characters unless a longer line can significantly improve the code readability and no information is hidden. 241Exception: If a one-line comment contains a command or URL of more than 120 characters, you can keep the line for ease in using copy, paste, and search with the **grep** command. Put the error information of preprocessor directives in one line to facilitate reading and understanding even if the line contains more than 120 characters. 242 243### Use Braces in Conditional Statements and Loop Statements 244 245**[Category]** Recommendation 246 247**[Description]** 248 249It is a best practice to add braces ({}) to the execution body of statements such as **if**, **for**, **do**, and **while**, because omitting the braces may cause errors and reduce code clarity. 250 251**[Incorrect Example]** 252 253``` 254if (condition) 255 console.log('success'); 256 257for (let idx = 0; idx < 5; ++idx) 258 console.log(idx); 259``` 260 261**[Correct Example]** 262 263``` 264if (condition) { 265 console.log('success'); 266} 267 268for (let idx = 0; idx < 5; ++idx) { 269 console.log(idx); 270} 271``` 272 273### Indent case and default in the switch Statement Block 274 275**[Category]** Recommendation 276 277**[Description]** 278 279Use two spaces to indent the case or default statement in a switch statement block. Use two spaces to indent the line feed statement after the switch label. 280 281**[Correct Example]** 282 283``` 284switch (condition) { 285 case 0: { 286 doSomething(); 287 break; 288 } 289 case 1: { 290 doOtherthing(); 291 break; 292 } 293 default: 294 break; 295} 296``` 297 298### Keep a Consistent Line Break Style for Expressions 299 300**[Category]** Recommendation 301 302**[Description]** 303 304When a statement is too long or difficult to read, start a new line at a proper position. 305During line breaking, always place operators at the end of lines, indicating that the operations are to be continued. This is also the default configurations of typical formatting tools. 306 307**[Correct Example]** 308 309``` 310// The if conditional statement exceeds the line width. 311if (userCount > MAX_USER_COUNT || 312 userCount < MIN_USER_COUNT) { 313 doSomething(); 314} 315``` 316 317### Do Not Put Multiple Variable Definitions and Assignment Statements in a Line 318 319**[Category]** Rule 320 321**[Description]** 322 323Each statement should declare only one variable. 324In this way, it is easier to add variable declarations and can avoid errors, because you do not need to consider changing a semicolon (;) to a comma (,). It is also easier for the debugger to debug variables one by one, rather than skipping all variables at a time. 325 326**[Incorrect Example]** 327 328``` 329let maxCount = 10, isCompleted = false; 330let pointX, pointY; 331pointX = 10; pointY = 0; 332``` 333 334**[Correct Example]** 335 336``` 337let maxCount = 10; 338let isCompleted = false; 339let pointX = 0; 340let pointY = 0; 341``` 342 343### Use Spaces to Highlight Keywords and Important Information 344 345**[Category]** Recommendation 346 347**[Description]** 348 349Use spaces to highlight keywords and important information. The general recommendations are as follows: 3501. Add a space between keywords such as **if**, **for**, **while**, and **switch** and the open parentheses. 3512. Do not add a space between the method name and the open parentheses of the parameter list when defining or calling the method. 3523. Add a space between the keyword **else** or **catch** and the close brace (}). 3534. Add a space before the open brace ({), except when: 354a. The open brace is used as the first parameter of a method or the first element in an array, for example, **foo({ name: 'abc' })**. 355b. The open brace is used in a template name, for example, **abc${name}**. 3565. Spaces are added before and after the binary operator (+, -, *, =, <, >, <=, >=, ===, !==, &&, ||). Spaces are added on both sides of the ternary operator (? :). 3576. Add a space after the comma in array initialization and the comma between multiple parameters in a method. 3587. Do not add a space before a comma (,) or semicolon (;). 3598. Do not add spaces inside the square brackets ([]) of an array. 3609. Do not contain multiple consecutive spaces. It is a bad practice if consecutive spaces in a line are not used for indentation. 361 362**[Incorrect Example]** 363 364``` 365// There is no space between if and the open parenthesis. 366if(isJedi) { 367 fight(); 368} 369 370// There is a space between the method name fight and the open parenthesis. 371function fight (): void { 372 console.log('Swooosh!'); 373} 374``` 375 376**[Correct Example]** 377 378``` 379// There is a space between if and the open parenthesis. 380if (isJedi) { 381 fight(); 382} 383 384// There is no space between the method name fight and the open parenthesis. 385function fight(): void { 386 console.log('Swooosh!'); 387} 388``` 389 390**[Incorrect Example]** 391 392``` 393if (flag) { 394 // ... 395}else { // There is no space between the close brace and else. 396 // ... 397} 398``` 399 400**[Correct Example]** 401 402``` 403if (flag) { 404 // ... 405} else { // There is a space between the close brace and else. 406 // ... 407} 408``` 409 410**[Correct Example]** 411 412``` 413function foo() { // There is a space before the open brace in the method declaration. 414 // ... 415} 416 417bar('attr', { // There is a space before the open brace. 418 age: '1 year', 419 sbreed: 'Bernese Mountain Dog', 420}); 421``` 422 423**[Correct Example]** 424 425``` 426const arr = [1, 2, 3]; // There is a space after the comma during array initialization. There is no space before the comma. 427myFunc(bar, foo, baz); // There is a space after the comma between multiple parameters of a method. There is no space before the comma. 428``` 429 430### Use Single Quotation Marks for Strings 431 432**[Category]** Recommendation 433 434**[Description]** 435 436To maintain code consistency and readability, use single quotes. 437 438**[Incorrect Example]** 439 440``` 441let message = "world"; 442console.log(message); 443``` 444 445**[Correct Example]** 446 447``` 448let message = 'world'; 449console.log(message); 450``` 451 452### If an Object Literal Has More Than Four Properties, Place Each of Them at Separate Lines 453 454**[Category]** Recommendation 455 456**[Description]** 457 458The properties of an object literal should be all placed at the same line or each at a separate line. If an object literal has more than four properties, place each of them at separate lines. 459 460**[Incorrect Example]** 461 462``` 463interface I { 464 name: string 465 age: number 466 value: number 467 sum: number 468 foo: boolean 469 bar: boolean 470} 471 472let obj: I = { name: 'tom', age: 16, value: 1, sum: 2, foo: true, bar: false } 473``` 474 475**[Correct Example]** 476 477``` 478interface I { 479 name: string 480 age: number 481 value: number 482 sum: number 483 foo: boolean 484 bar: boolean 485} 486 487let obj: I = { 488 name: 'tom', 489 age: 16, 490 value: 1, 491 sum: 2, 492 foo: true, 493 bar: false 494} 495``` 496 497### Put else or catch in the Same Line as the Close Parenthesis of the if or try Code Block 498 499**[Category]** Recommendation 500 501**[Description]** 502 503In conditional statements, place **else** in the same line as the close parenthesis of the **if** code block. Similarly, in exception handling statements, place **catch** in the same line as the close parenthesis of the **try** code block. 504 505**[Incorrect Example]** 506 507``` 508if (isOk) { 509 doThing1(); 510 doThing2(); 511} 512else { 513 doThing3(); 514} 515``` 516 517**[Correct Example]** 518 519``` 520if (isOk) { 521 doThing1(); 522 doThing2(); 523} else { 524 doThing3(); 525} 526``` 527 528**[Incorrect Example]** 529 530``` 531try { 532 doSomething(); 533} 534catch (err) { 535 // Error handling. 536} 537``` 538 539**[Correct Example]** 540 541``` 542try { 543 doSomething(); 544} catch (err) { 545 // Error handling. 546} 547``` 548 549### Put the Open Brace and the Statement in the Same Line 550 551**[Category]** Recommendation 552 553**[Description]** 554 555Follow a consistent style of using braces in the project. You are advised to put the open brace ({) and the control or declaration statement in the same line. 556 557**[Incorrect Example]** 558 559``` 560function foo() 561{ 562 // ... 563} 564``` 565 566**[Correct Example]** 567 568``` 569function foo() { 570 // ... 571} 572``` 573 574## Programming Practices 575 576### Add Accessible Modifiers for Class Properties 577 578**[Category]** Recommendation 579 580**[Description]** 581 582ArkTS provides the **private**, **protected**, and **public** access modifiers. The default accessible modifier of a property is **public**. Appropriate accessible modifiers can improve code security and readability. Note: If a class contains the **private** attribute, the class cannot be initialized through object literals. 583 584**[Incorrect Example]** 585 586``` 587class C { 588 count: number = 0 589 590 getCount(): number { 591 return this.count 592 } 593} 594``` 595 596**[Correct Example]** 597 598``` 599class C { 600 private count: number = 0 601 602 public getCount(): number { 603 return this.count 604 } 605} 606``` 607 608### Do Not Omit 0s Before and After the Decimal Point of a Floating-Point Number 609 610**[Category]** Recommendation 611 612**[Description]** 613 614In ArkTS, a floating-point number must contain a decimal point, but no digit is required before or after the decimal point. However, using digits before and after the decimal point can improve code readability. 615 616**[Incorrect Example]** 617 618``` 619const num = .5; 620const num = 2.; 621const num = -.7; 622``` 623 624**[Correct Example]** 625 626``` 627const num = 0.5; 628const num = 2.0; 629const num = -0.7; 630``` 631 632### Use Number.isNaN() to Check Whether a Variable Is Number.NaN 633 634**[Category]** Rule 635 636**[Description]** 637 638In ArkTS, **Number.NaN** is a particular value of a numeric data type. It represents a non-numeric value in the double-precision 64-bit format, as defined in the IEEE floating-point standard. 639**Number.NaN** is unique in ArkTS because it is not equal to any value, including itself. For example, the result of comparison with **Number.NaN** is confusing, as the values of **Number.NaN !== Number.NaN** and **Number.NaN != Number.NaN** are both **true**. 640Therefore, you must use **Number.isNaN()** to check whether a value is **Number.NaN**. 641 642**[Incorrect Example]** 643 644``` 645if (foo == Number.NaN) { 646 // ... 647} 648 649if (foo != Number.NaN) { 650 // ... 651} 652``` 653 654**[Correct Example]** 655 656``` 657if (Number.isNaN(foo)) { 658 // ... 659} 660 661if (!Number.isNaN(foo)) { 662 // ... 663} 664``` 665 666### Preferentially Use Array Object Methods for Array Traversal 667 668**[Category]** Rule 669 670**[Description]** 671 672To traverse an array, preferentially use the methods provided by **Array**, such as **forEach()**, **map()**, **every()**, **filter()**, **find()**, **findIndex()**, **reduce()**, and **some()**. 673 674**[Incorrect Example]** 675 676``` 677const numbers = [1, 2, 3, 4, 5]; 678// Use for to traverse an existing array to generate a new array. 679const increasedByOne: number[] = []; 680for (let i = 0; i < numbers.length; i++) { 681 increasedByOne.push(numbers[i] + 1); 682} 683``` 684 685**[Correct Example]** 686 687``` 688const numbers = [1, 2, 3, 4, 5]; 689// Better: Use the map method. 690const increasedByOne: number[] = numbers.map(num => num + 1); 691``` 692 693### Do Not Assign Values in Control Conditional Expressions 694 695**[Category]** Rule 696 697**[Description]** 698 699Control conditional expressions are usually used in **if**, **while**, **for**, and **?:** statements. 700Assigning values in this type of expression often leads to unexpected behavior and poor code readability. 701 702**[Incorrect Example]** 703 704``` 705// It is difficult to understand the value assignment in the control conditional expression. 706if (isFoo = false) { 707 // ... 708} 709``` 710 711**[Correct Example]** 712 713``` 714const isFoo = false; // Assign a value above and directly use it in the if statement. 715if (isFoo) { 716 // ... 717} 718``` 719 720### Do Not Use return, break, continue, or throw in a finally Code Block 721 722**[Category]** Rule 723 724**[Description]** 725 726If the **return**, **break**, **continue**, or **throw** statement is used in a **finally** code block or an exception that arises during method calling is not handled, the **finally** code block cannot properly stop. An abnormally stopped **finally** code block affects the throwing of exceptions in a **try** or **catch** block, and may affect the return value of a method. Therefore, ensure that the **finally** code block can stop properly. 727 728**[Incorrect Example]** 729 730``` 731function foo() { 732 try { 733 // ... 734 return 1; 735 } catch (err) { 736 // ... 737 return 2; 738 } finally { 739 return 3; 740 } 741} 742``` 743 744**[Correct Example]** 745 746``` 747function foo() { 748 try { 749 // ... 750 return 1; 751 } catch (err) { 752 // ... 753 return 2; 754 } finally { 755 console.log('XXX!'); 756 } 757} 758``` 759 760### Do Not Use ESObject 761 762**[Category]** Recommendation 763 764**[Description]** 765 766**ESObject** is mainly used for type annotation in ArkTS and TS/JS cross-language calls. Using it in other scenarios introduces unnecessary cross-language calls and causes extra performance overhead. 767 768**[Incorrect Example]** 769 770``` 771// lib.ets 772export interface I { 773 sum: number 774} 775 776export function getObject(value: number): I { 777 let obj: I = { sum: value }; 778 return obj 779} 780 781// app.ets 782import { getObject } from 'lib' 783let obj: ESObject = getObject(123); 784``` 785 786**[Correct Example]** 787 788``` 789// lib.ets 790export interface I { 791 sum: number 792} 793 794export function getObject(value: number): I { 795 let obj: I = { sum: value }; 796 return obj 797} 798 799// app.ets 800import { getObject, I } from 'lib' 801let obj: I = getObject(123); 802``` 803 804### Use T[] for the Array Type 805 806**[Category]** Recommendation 807 808**[Description]** 809 810ArkTS provides two array types: **T[]** and **Array\<T>**. To improve code readability, you are advised to use **T[]** to represent all array types. 811 812**[Incorrect Example]** 813 814``` 815let x: Array<number> = [1, 2, 3]; 816let y: Array<string> = ['a', 'b', 'c']; 817``` 818 819**[Correct Example]** 820 821``` 822// Use the T[] syntax. 823let x: number[] = [1, 2, 3]; 824let y: string[] = ['a', 'b', 'c']; 825``` 826