8. Statements

Statements are designed to control the execution:

statement:
    expressionStatement
    | block
    | localDeclaration
    | ifStatement
    | loopStatement
    | breakStatement
    | continueStatement
    | returnStatement
    | switchStatement
    | throwStatement
    | tryStatement
    | assertStatement
    ;

8.1. Normal and Abrupt Statement Execution

The actions that every statement performs in a normal mode of execution are specific for the particular kind of that statement. The normal modes of evaluation for each kind of statement are described in the following sections.

A statement execution is considered to complete normally if the desired action is performed without an exception or an error being thrown.

On the contrary, a statement execution is considered to complete abruptly if it causes an exception or an error to be thrown.


8.2. Expression Statements

Any expression can be used as a statement:

expressionStatement:
    expression
    ;

The execution of a statement leads to the execution of the expression. The result of such execution is discarded.


8.3. Block

A sequence of statements enclosed in balanced braces forms a block:

block:
    '{' statement* '}'
    ;

The execution of a block means that all block statements, except type declarations, are executed one by one in the textual order of their appearance within the block if no exception, error, or return occurs.

If a block is the body of a functionDeclaration or a classMethodDeclaration, which was declared implicitly or explicitly with return type void, then the block can contain no return statement at all. Such a block is equivalent to a block ending in the return statement, and is executed accordingly.


8.4. Local Declarations

Local declarations define new mutable or immutable variables or types within the enclosing context.

Let and const declarations have the initialization part that presumes execution, and actually act as statements:

localDeclaration:
    variableDeclaration
    | constantDeclaration
    | typeDeclaration
    ;

The visibility of a local declaration name is determined by the function (method) and block scope rules (see Scopes).


8.5. if Statements

An if statement allows executing alternative statements (if provided) under certain conditions:

ifStatement:
    'if' '(' expression ')' statement1
    ('else' statement2)?
    ;

If an expression represents a condition and is successfully evaluated as true, then statement1 is executed. Otherwise, statement2 is executed (if provided). A compile-time error occurs if the expression type is not boolean.

Any else matches the first if of an if statement:

1 if (Cond1)
2 if (Cond2) statement1
3 else statement2 // Executes only if: Cond1 && !Cond2

A list of statements in braces (see Block) is used to combine the else part with the first if:

1 if (Cond1) {
2   if (Cond2) statement1
3 }
4 else statement2 // Executes if: !Cond1

8.6. loop Statements

ArkTS has four kinds of loops. A loop of each kind can have an optional loop label that can be used only by break and continue statements contained in the body of the loop. The label is characterized by an identifier as shown below:

loopStatement:
    (identifier ':')?
    whileStatement
    | doStatement
    | forStatement
    | forOfStatement
    ;

8.7. while Statements and do Statements

A while statement and a do statement evaluate an expression and execute a statement repeatedly till the expression value is true. The key difference is that whileStatement first evaluates and checks the expression value, and doStatement first executes the statement:

whileStatement:
    'while' '(' expression ')' statement
    ;

doStatement
    : 'do' statement 'while' '(' expression ')'
    ;

8.8. for Statements

forStatement:
    'for' '(' forInit? ';' expression? ';' forUpdate? ')' statement
    ;

forInit:
    expressionSequence
    | variableDeclarations
    ;

forUpdate:
    expressionSequence
    ;
 1 // existing variable
 2 let i: number
 3 for (i = 1; i < 10; i++) {
 4   console.log(i)
 5 }
 6
 7 // new variable, explicit type:
 8 for (let i: number = 1; i < 10; i++) {
 9   console.log(i)
10 }
11
12 // new variable, implicit type
13 // inferred from variable declaration
14 for (let i = 1; i < 10; i++) {
15   console.log(i)
16 }

8.9. for-of Statements

A for-of loop iterates elements of array or string, or an instance of iterable class or interface (see Iterable Types):

forOfStatement:
    'for' '(' forVariable 'of' expression ')' statement
    ;

forVariable:
    identifier | ('let' | 'const') identifier (':' type)?
    ;

A compile-time error occurs if the type of an expression is not array, string, or iterable type.

The execution of a for-of loop starts with the evaluation of expression. If the evaluation is successful, then the resultant expression is used for loop iterations (execution of the statement). On each iteration, forVariable is set to successive elements of the array, string, or result of class iterator advancing.

If forVariable has the modifiers let or const, then a new variable is used inside the loop. Otherwise, the variable is as declared above. The modifier const prohibits assignments into forVariable, while let allows modifications.

Explicit type annotation of forVariable is allowed as an experimental feature (see For-of Type Annotation).

 1 // existing variable 'ch'
 2 let ch : char
 3 for (ch of "a string object") {
 4   console.log(ch)
 5 }
 6
 7 // new variable 'ch', its type is
 8 // inferred from expression
 9 for (let ch of "a string object") {
10   console.log(ch)
11 }
12
13 // new variable 'element', its type is
14 // inferred from expression, and it
15 // cannot be assigned with a new value
16 // in the loop body
17 for (const element of [1, 2, 3]) {
18   console.log(element)
19 }

8.10. break Statements

A break statement transfers control out of the enclosing loopStatement or switchStatement:

breakStatement:
    'break' identifier?
    ;

A break statement with the label identifier transfers control out of the enclosing statement with the same label identifier. A compile-time error occurs if such a statement is not found within the body of the surrounding function or method.

A statement without a label transfers control out of the innermost enclosing switch, while, do, for, or for-of statement.

A compile-time error occurs if the breakStatement is not found within loopStatment or switchStatement.


8.11. continue Statements

A continue statement stops the execution of the current loop iteration, and transfers control to the next iteration. Appropriate checks of loop exit conditions depend on the kind of the loop.

continueStatement:
    'continue' identifier?
    ;

A continue statement with the label identifier transfers control out of the enclosing loop statement with the same label identifier. A compile-time error occurs if such a statement is not found within the body of the surrounding function or method.

A compile-time error occurs if no continueStatement is found within loopStatment.


8.12. return Statements

A return statement can have or not have an expression.

returnStatement:
    'return' expression?
    ;

A ‘return expression’ statement can only occur inside a function or a method body.

A return statement (with no expression) can occur in one of the following situations:

  • Inside a class initializer;

  • Inside a constructor body; or

  • Inside a function or a method body with return type void.

A compile-time error occurs if a return statement is found in:

  • Top-level statements (see Top-Level Statements);

  • Class initializers (see Class Initializer) and constructors (see Constructor Declaration), where it has an expression;

  • A function or a method with return type void, where it has an expression;

  • A function or a method with a non-void return type, where it has no expression.

The execution of returnStatement leads to the termination of the surrounding function or method. If an expression is provided, the resultant value is the evaluated expression.

In case of constructors, class initializers, and top-level statements, the control is transferred out of the scope of the construction in question, but no result is required. Other statements of the surrounding function, method body, class initializer, or top-level statement are not executed.


8.13. switch Statements

A switch statement transfers control to a statement or a block by using the result of successful evaluation of the value of a switch expression.

switchStatement:
    (identifier ':')? 'switch' '(' expression ')' switchBlock
    ;

switchBlock
    : '{' caseClause* defaultClause? caseClause* '}'
    ;

caseClause
    : 'case' expression ':' (statement+ | block)?
    ;

defaultClause
    : 'default' ':' (statement+ | block)?
    ;

The switch expression type must be of type char, byte, short, int, long, Char, Byte, Short, Int, Long, string, or enum.

A compile-time error occurs if not all of the following is true:

  • Every case expression type associated with a switch statement is compatible (see Type Compatibility) with the type of the switch statement’s expression.

  • In a switch statement expression of type enum, every case expression associated with the switch statement is of type enum.

  • No two case expressions associated with the switch statement have identical values.

  • No case expression associated with the switch statement is null.


 1 let arg = prompt("Enter a value?");
 2 switch (arg) {
 3   case '0':
 4   case '1':
 5     alert('One or zero')
 6     break
 7   case '2':
 8     alert('Two')
 9     break
10   default:
11     alert('An unknown value')
12 }

The execution of a switch statement starts from the evaluation of the switch expression. If the evaluation result is of type Char, Byte, Short, or Int, then the unboxing conversion follows.

Otherwise, the value of the switch expression is compared repeatedly to the value of each case expression.

If a case expression value equals the value of the switch expression in terms of the operator ‘\(==\)’, then the case label matches.

However, if the expression value is a string, then the equality for strings determines the equality.


8.14. throw Statements

A throw statement causes exception or error to be thrown (see Error Handling). It immediately transfers control, and can exit multiple statements, constructors, functions, and method calls until a try statement (see try Statements) is found that catches the thrown value. If no try statement is found, then UncatchedExceptionError is thrown.

throwStatement:
    'throw' expression
    ;

The expression’s type must be assignable (see Assignment) to type Exception or Error. Otherwise, a compile-time error occurs.

This implies that the thrown object is never null.

It is necessary to check at compile time that a throw statement, which throws an exception, is placed in the try block of a try statement, or in a throwing function (see Throwing Functions). Errors can be thrown at any place in the code.


8.15. try Statements

A try statement runs blocks of code, and provides sets of catch clauses to handle different exceptions and errors (see Error Handling).

tryStatement:
      'try' block catchClauses finallyClause?
      ;

catchClauses:
      typedCatchClause* catchClause?
      ;

catchClause:
      'catch' '(' identifier ')' block
      ;

typedCatchClause:
      'catch' '(' identifier ':' typeReference ')' block
      ;

finallyClause:
      'finally' block
      ;

The ArkTS programming language supports multiple typed catch clauses as an experimental feature (see try Statements).

A try statement must contain either a finally clause, or at least one catch clause. Otherwise, a compile-time error occurs.

If the try block completes normally, then no action is taken, and no catch clause block is executed.

If an error is thrown in the try block directly or indirectly, then the control is transferred to the catch clause.


8.15.1. catch Clause

A catch clause consists of two parts:

  • Catch identifier that provides access to the object associated with the error thrown; and

  • Block of code that handles the situation.

The type of catch identifier is Object.

See Multiple Catch Clauses in Try Statements for the details of typed catch clause.


 1 class ZeroDivisor extends Error {}
 2
 3 function divide(a: number, b: number): number {
 4   if (b == 0)
 5     throw new ZeroDivisor()
 6   return a / b
 7 }
 8
 9 function process(a: number, b: number): number {
10   try {
11     let res = divide(a, b)
12
13     // further processing ...
14   }
15   catch (e) {
16     return e instanceof ZeroDivisor? -1 : 0
17   }
18 }

A catch clause handles all errors at runtime. It returns ‘-1’ for the ZeroDivisor, and ‘0’ for all other errors.


8.15.2. finally Clause

A finally clause defines the set of actions in the form of a block to be executed without regard to whether a try-catch completes normally or abruptly.

finallyClause:
    'finally' block
    ;

A finally block is executed without regard to how (by reaching exception, error, return, or try-catch end) the program control is transferred out. The finally block is particularly useful to ensure proper resource management.

Any required actions (e.g., flush buffers and close file descriptors) can be performed while leaving the try-catch:

class SomeResource {
  // some API
  // ...
  close() : void {}
}

function ProcessFile(name: string) {
  let r = new SomeResource()
  try {
    // some processing
  }
  finally {
    // finally clause will be executed after try-catch is
        executed normally or abruptly
    r.close()
  }
}

8.15.3. try Statement Execution

  1. A try block and the entire try statement complete normally if no catch block is executed. The execution of a try block completes abruptly if an exception or an error is thrown inside the try block. Catch clauses are checked in the textual order of their position in the source code.

  2. The execution of a try block completes abruptly if exception or error x is thrown inside the try block. If the runtime type of x is compatible (see Type Compatibility) with the exception class of the exception parameter (i.e., the catch clause matches x), and the execution of the body of the catch clause completes normally, then the entire try statement completes normally. Otherwise, the try statement completes abruptly.

  3. If no catch clause can handle an exception or an error, then those propagate to the surrounding scope. If the surrounding scope is a function, method, or constructor, then the execution depends on whether the surrounding scope is a throwing function (see Throwing Functions). If so, then the exception propagates to the caller context. Otherwise, UncatchedExceptionError is thrown.


8.16. Assert Statements

The assert statements are described in the chapter Experimental Features (see Assert Statements).