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