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