1# Introduction 2 3Welcome to the tutorial for ArkTS, a TypeScript-based programming language designed specifically to build high-performance mobile applications! 4 5ArkTS is optimized to provide better performance and efficiency, while still maintaining the familiar syntax of TypeScript. 6 7As mobile devices continue to become more prevalent in our daily lives, there is a growing need for programming languages optimized for the mobile environment. Many current programming languages were not designed with mobile devices in mind, resulting in slow and inefficient applications that drain battery life. ArkTS has been specifically designed to address such concerns by prioritizing higher execution efficiency. 8 9ArkTS is based on the popular programming language TypeScript that extends JavaScript by adding type definitions. TypeScript is well-loved by many developers as it provides a more structured approach to coding in JavaScript. ArkTS aims to keep the look and feel of TypeScript to enable a seamless transition for the existing TypeScript developers, and to let mobile developers learn ArkTS quickly. 10 11One of the key features of ArkTS is its focus on low runtime overhead. 12ArkTS imposes stricter limitations on the TypeScript's dynamically typed features, reducing runtime overhead and allowing faster execution. By eliminating the dynamically typed features from the language, ArkTS code can be compiled ahead-of-time more efficiently, resulting in faster application startup and lower power consumption. 13 14Interoperability with JavaScript was a critical consideration in the ArkTS language design. Many mobile app developers already have TypeScript and JavaScript code and libraries they would want to reuse. ArkTS has been designed for seamless JavaScript interoperability, making it easy for the developers to integrate the JavaScript code into their applications and vice versa. This will allow the developers to use their existing codebases and libraries to leverage the power of our new language. 15 16To ensure best experience for UI app development for OpenHarmony ecosystem, ArkTS provides support for ArkUI, including its declarative syntax and other features. Since this feature is outside the scope of the "stock" TypeScript, a verbose ArkUI example is provided in a separate chapter. 17 18This tutorial will guide you through the core features, syntax, and best practices of ArkTS. After reading this tutorial through the end, you will be able to build performant and efficient mobile applications in ArkTS. 19 20# The Basics 21 22## Declarations 23 24Declarations in ArkTS introduce: 25 26- Variables 27- Constants 28- Functions 29- Types 30 31### Variable Declaration 32 33A declaration starting with the keyword `let` introduces a variable which can have different values during program execution. 34 35```typescript 36let hi: string = 'hello' 37hi = 'hello, world' 38``` 39 40### Constant Declaration 41 42A declaration starting with the keyword `const` introduces a read-only constant that can be assigned only once. 43 44```typescript 45const hello: string = 'hello' 46``` 47 48A compile-time error occurs if a new value is assigned to a constant. 49 50### Automatic Type Inference 51 52As ArkTS is a statically typed language, the types of all entities, like variables and constants, have to be known at compile time. 53 54However, developers do not need to explicitly specify the type of a declared entity if a variable or a constant declaration contains an initial value. 55 56All cases that allow the type to be inferred automatically are specified in the ArkTS Specification. 57 58Both variable declarations are valid, and both variables are of the `string` type: 59 60```typescript 61let hi1: string = 'hello' 62let hi2 = 'hello, world' 63``` 64 65## Types 66 67`Class`, `interface`, `function`, `enum`, `union` types, and type `aliases` are described in the corresponding sections. 68 69### Numeric Types 70 71ArkTS has `number` and `Number` numeric types. Any integer and floating-point values can be assigned to a variable of these types. 72 73Numeric literals include integer literals and floating-point literals 74with the decimal base. 75 76Integer literals include the following: 77 78* Decimal integers that consist of a sequence of digits. For example: `0`, `117`, `-345`. 79* Hexadecimal integers that start with 0x (or 0X), and can contain digits (0-9) and letters a-f or A-F. For example: `0x1123`, `0x00111`, `-0xF1A7`. 80* Octal integers that start with 0o (or 0O) and can only contain digits (0-7). For example: `0o777`. 81* Binary integers that start with 0b (or 0B), and can only contain the digits 0 and 1. For example: `0b11`, `0b0011`, `-0b11`. 82 83A floating-point literal includes the following: 84 85* Decimal integer, optionally signed (i.e., prefixed with "+" or "-"); 86* Decimal point ("."). 87* Fractional part (represented by a string of decimal digits). 88* Exponent part that starts with "e" or "E", followed by an optionally signed (i.e., prefixed with "+" or "-") integer. 89 90Example: 91 92```typescript 93let n1 = 3.14 94let n2 = 3.141592 95let n3 = .5 96let n4 = 1e10 97 98function factorial(n: number): number { 99 if (n <= 1) { 100 return 1 101 } 102 return n * factorial(n - 1) 103} 104``` 105 106### `Boolean` 107 108The `boolean` type represents logical values that are either `true` or `false`. 109 110Usually variables of this type are used in conditional statements: 111 112```typescript 113let isDone: boolean = false 114 115// ... 116 117if (isDone) { 118 console.log ('Done!') 119} 120``` 121 122### `String` 123 124A `string` is a sequence of characters; some characters can be set by using escape sequences. 125 126A `string` literal consists of zero or more characters enclosed in single (') or double quotes ("). The special form of string literals are template literals enclosed in backtick quotes (\`). 127 128```typescript 129let s1 = 'Hello, world!\n' 130let s2 = 'this is a string' 131let a = 'Success' 132let s3 = `The result is ${a}` 133``` 134 135### `Void` Type 136 137The `void` type is used to specify that a function does not return a value. 138This type has the only one value which is also `void`. As `void` is 139a reference type, it can be used as type argument for generic types. 140 141```typescript 142class Class<T> { 143 //... 144} 145let instance: Class <void> 146``` 147 148### `Object` Type 149 150An `Object` class type is a base type for all reference types. Any value, including values of primitive types (they will be automatically boxed), can be directly assigned to variables of the type `Object`. 151 152### `Array` Type 153 154An `array` is an object comprised of elements of data types assignable to the element type specified in the array declaration. 155A value of an `array` is set by using *array composite literal*, that is a list of zero or more expressions enclosed in square brackets ([]). Each expression represents an element of the `array`. The length of the `array` is set by the number of expressions. Index of the first array element is 0. 156 157The following example creates the `array` with three elements: 158 159```typescript 160let names: string[] = ['Alice', 'Bob', 'Carol'] 161``` 162 163### `Enum` Type 164 165An `enum` type is a value type with a defined set of named values called enum constants. 166In order to be used, an `enum` constant must be prefixed with an enum `type` name. 167 168```typescript 169enum Color { Red, Green, Blue } 170let c: Color = Color.Red 171``` 172 173A constant expression can be used to explicitly set the value of an `enum` constant. 174 175```typescript 176enum Color { White = 0xFF, Grey = 0x7F, Black = 0x00 } 177let c: Color = Color.Black 178``` 179 180### `Union` Type 181 182A `union` type is a reference type which is created as a combination of other types. Values of union types can be valid values of all types a union was created from. 183 184```typescript 185class Cat { 186 // ... 187} 188class Dog { 189 // ... 190} 191class Frog { 192 // ... 193} 194type Animal = Cat | Dog | Frog | number 195// Cat, Dog, and Frog are some types (class or interface ones) 196 197let animal: Animal = new Cat() 198animal = new Frog() 199animal = 42 200// One may assign the variable of the union type with any valid value 201``` 202 203There are different mechanisms to get a value of a particular type from a union. 204 205Example: 206 207```typescript 208class Cat { sleep () {}; meow () {} } 209class Dog { sleep () {}; bark () {} } 210class Frog { sleep () {}; leap () {} } 211 212type Animal = Cat | Dog | Frog | number 213 214let animal: Animal = new Cat() 215if (animal instanceof Frog) { 216 let frog: Frog = animal as Frog // animal is of type Frog here 217 animal.leap() 218 frog.leap() 219 // As a result frog leaps twice 220} 221 222animal.sleep () // Any animal can sleep 223``` 224 225### Type `Aliases` 226 227Type `aliases` provides names for anonymous types (array, function, object literal or union types) or alternative names for existing types. 228 229```typescript 230type Matrix = number[][] 231type Handler = (s: string, no: number) => string 232type Predicate <T> = (x: T) => Boolean 233type NullableObject = Object | null 234``` 235 236## Operators 237 238### Assignment Operators 239 240Simple assignment operator '=' is used as in "x = y". 241 242Compound assignment operators combine an assignment with an operator, where `x op = y` equals `x = x op y`. 243 244Compound assignment operators are as follows: `+=`, `-=`, `*=`, `/=`, `%=`, `<<=`, `>>=`, `>>>=`, `&=`, `|=`, `^=`. 245 246### Comparison Operators 247 248| Operator | Description | 249| -------- | ------------------------------------------------------------ | 250| `==` | Returns true if both operands are equal. | 251| `!=` | Returns true if both operands are not equal. | 252| `>` | Returns true if the left operand is greater than the right. | 253| `>=` | Returns true if the left operand is greater than or equal to the right. | 254| `<` | Returns true if the left operand is less than the right. | 255| `<=` | Returns true if the left operand is less than or equal to the right. | 256### Arithmetic Operators 257 258Unary operators are `-`, `+`, `--` and `++`. 259 260Binary operators are as follows: 261 262| Operator | Description | 263|------------|--------------------------| 264| `+` | addition | 265| `-` | subtraction | 266| `*` | multiplication | 267| `/` | division | 268| `%` | remainder after division | 269### Bitwise Operators 270 271| Operator | Description | 272|------------|-----------------------------------------------------------------------------------------------------------------| 273| `a & b` | Bitwise AND: sets each bit to 1 if the corresponding bits of both operands are 1, otherwise to 0. | 274| `a \| b` | Bitwise OR: sets each bit to 1 if at least one of the corresponding bits of both operands is 1, otherwise to 0. | 275| `a ^ b` | Bitwise XOR: sets each bit to 1 if the corresponding bits of both operands are different, otherwise to 0. | 276| `~ a` | Bitwise NOT: inverts the bits of the operand. | 277| `a << b` | Shift left: shifts the binary representation of *a* to the left by *b* bits. | 278| `a >> b` | Arithmetic right shift: shifts the binary representation of *a* to the right by *b* bits with sign-extension. | 279| `a >>> b` | Logical right shift: shifts the binary representation of *a* to the right by *b* bits with zero-extension. | 280### Logical Operators 281 282| Operator | Description | 283|------------|---------------| 284| `a && b` | Logical AND | 285| `a \|\| b` | Logical OR | 286| `! a` | Logical NOT | 287## Statements 288 289### `If` Statements 290 291An `if` statement is used to execute a sequence of statements when a logical condition is `true`, or another set of statements (if provided) otherwise. 292 293The `else` part can also contain more `if` statements. 294 295An `if` statement looks as follows: 296 297```typescript 298if (condition1) { 299 // statements1 300} else if (condition2) { 301 // statements2 302} else { 303 // else_statements 304} 305``` 306 307All conditional expressions must be of the type `boolean` or other types (`string`, `number`, etc.). For types other than `boolean`, implicit conversion rules apply: 308 309```typescript 310let s1 = 'Hello' 311if (s1) { 312 console.log(s1) // prints 'Hello' 313} 314 315let s2 = 'World' 316if (s2.length != 0) { 317 console.log(s2) // prints 'World' 318} 319``` 320 321### `Switch` Statements 322 323A `switch` statement is used to execute a sequence of statements that match the value of a switch expression. 324 325A `switch` statement looks as follows: 326 327```typescript 328switch (expression) { 329 case label1: // will be executed if label1 is matched 330 // ... 331 // statements1 332 // ... 333 break; // Can be omitted 334 case label2: 335 case label3: // will be executed if label2 or label3 is matched 336 // ... 337 // statements23 338 // ... 339 break; // Can be omitted 340 default: 341 // default_statements 342} 343``` 344 345The `switch` expression type must be of `number`, `enum` or `string` types. 346 347Each label must be either a constant expression or the name of an enum constant. 348 349If the value of a `switch` expression equals the value of some label, then the corresponding statements are executed. 350 351If there is no match, and the `switch` has the default clause, then the default statements are executed. 352 353An optional `break` statement allows you to break out of the `switch` and continue executing the statement that follows the `switch`. 354 355If there is no `break`, then the next statements in the `switch` are executed. 356 357### Conditional Expressions 358 359The conditional expression `? :` uses the `boolean` value of the first expression to decide which of two other expressions to evaluate. 360 361A conditional expression looks as follows: 362 363```typescript 364condition ? expression1 : expression2 365``` 366 367The condition must be a logical expression. If that logical expression is `true`, then the first expression is used as the result of the ternary expression; otherwise, the second expression is used. 368 369Example: 370 371```typescript 372let isValid = Math.random() > 0.5 ? true : false 373let message = isValid ? 'Valid' : 'Failed' 374``` 375 376### `For` Statements 377 378A `for` statement is executed repeatedly until the specified loop exit condition is `false`. 379 380A `for` statement looks as follows: 381 382```typescript 383for ([init]; [condition]; [update]) { 384 statements 385} 386``` 387 388When a `for` statement is executed, the following process takes place: 389 3901. An `init` expression is executed, if any. This expression usually initializes one or more loop counters. 3912. The condition is evaluated. If the value of condition is `true`, or if the conditional expression is omitted, then the statements in the `for` body are to be executed. If the value of condition is `false`, then the `for` loop terminates. 3923. The statements of the `for` body are executed. 3934. If there is an `update` expression, then the `update` expression is executed. 3945. Go back to step 2. 395 396Example: 397 398```typescript 399let sum = 0 400for (let i = 0; i < 10; i += 2) { 401 sum += i 402} 403``` 404 405### `For-of` Statements 406 407`for-of` statements are used to iterate over an array or string. 408 409A `for-of` statement looks as follows: 410 411```typescript 412for (forVar of expression) { 413 statements 414} 415``` 416 417Example: 418 419```typescript 420for (let ch of 'a string object') { /* process ch */ } 421``` 422 423### `While` Statements 424 425A `while` statement has its body statements executed as long as the specified condition evaluates to `true`. 426 427A `while` statement looks as follows: 428 429```typescript 430while (condition) { 431 statements 432} 433``` 434 435The condition must be a logical expression. 436 437Example: 438 439```typescript 440let n = 0 441let x = 0 442while (n < 3) { 443 n++ 444 x += n 445} 446``` 447 448### `Do-while` Statements 449 450`do-while` statements are executed repetitively until a specified condition evaluates to false. 451 452A `do-while` statement looks as follows: 453 454```typescript 455do { 456 statements 457} while (condition) 458``` 459 460The condition must be a logical expression. 461 462Example: 463 464```typescript 465let i = 0 466do { 467 i += 1 468} while (i < 10) 469``` 470 471### `Break` Statements 472 473A `break` statement is used to terminate any `loop` statement or `switch`. 474 475Example: 476 477```typescript 478let x = 0 479while (true) { 480 x++; 481 if (x > 5) { 482 break; 483 } 484} 485``` 486 487A `break` statement with a label identifier transfers control out of the enclosing statement to the one which has the same label identifier. 488 489Example: 490 491```typescript 492let x = 1 493label: while (true) { 494 switch (x) { 495 case 1: 496 // statements 497 break label // breaks the while 498 } 499} 500``` 501 502### `Continue` Statements 503 504A `continue` statement stops the execution of the current loop iteration and passes control to the next iteration. 505 506Example: 507 508```typescript 509let sum = 0 510for (let x = 0; x < 100; x++) { 511 if (x % 2 == 0) { 512 continue 513 } 514 sum += x 515} 516``` 517 518### `Throw` and `Try` Statements 519 520A `throw` statement is used to throw an exception or an error: 521 522```typescript 523throw new Error('this error') 524``` 525 526A `try` statement is used to catch and handle an exception or an error: 527 528```typescript 529try { 530 // try block 531} catch (e) { 532 // handle the situation 533} 534``` 535 536The example below shows the `throw` and `try` statements used to handle the zero division case: 537 538```typescript 539class ZeroDivisor extends Error {} 540 541function divide (a: number, b: number): number{ 542 if (b == 0) throw new ZeroDivisor() 543 return a / b 544} 545 546function process (a: number, b: number) { 547 try { 548 let res = divide(a, b) 549 console.log(res) 550 } catch (x) { 551 console.log('some error') 552 } 553} 554``` 555 556`finally` clause is also supported: 557 558```typescript 559function processData(s: string) { 560 let error: Error | null = null 561 562 try { 563 console.log('Data processed: ', s) 564 // ... 565 // Throwing operations 566 // ... 567 } catch (e) { 568 error = e as Error 569 // ... 570 // More error handling 571 // ... 572 } finally { 573 if (error != null) { 574 console.log(`Error caught: input='${s}', message='${error.message}'`) 575 } 576 } 577} 578``` 579 580# Functions 581 582## Function Declarations 583 584A function declaration introduces a named function, specifying its name, parameters, return type and body. 585 586Below is a simple function with two string parameters and string return type: 587 588```typescript 589function add(x: string, y: string): string { 590 let z: string = `${x} ${y}` 591 return z 592} 593``` 594 595For every parameter its type annotation must be specified. 596An optional parameter allows you to omit the corresponding argument when calling a function. The last parameter of a function can be a rest parameter. 597 598## Optional Parameters 599 600An optional parameter has the form `name?: Type`. 601 602```typescript 603function hello(name?: string) { 604 if (name == undefined) { 605 console.log('Hello!') 606 } else { 607 console.log(`Hello, ${name}!`) 608 } 609} 610``` 611 612Another form contains an expression that specifies a default value. 613If the corresponding argument to such parameter is omitted in a function call, then this parameter's value is default. 614 615```typescript 616function multiply(n: number, coeff: number = 2): number { 617 return n * coeff 618} 619multiply(2) // returns 2*2 620multiply(2, 3) // returns 2*3 621``` 622 623## The Rest Parameter 624 625The last parameter of a function can be a rest parameter. It allows functions or methods to take unlimited number of arguments. 626 627```typescript 628function sum(...numbers: number[]): number { 629 let res = 0 630 for (let n of numbers) 631 res += n 632 return res 633} 634 635sum() // returns 0 636sum(1, 2, 3) // returns 6 637``` 638 639## Return Types 640 641If function return type can be inferred from its body content, then it can be omitted from the function declaration. 642 643```typescript 644// Explicit return type 645function foo(): string { return 'foo' } 646 647// Implicit return type inferred as string 648function goo() { return 'goo' } 649``` 650 651The return type of a function that does not need to return a value can be explicitly specified as `void` or omitted altogether. No return statement is needed for such functions. 652 653Both notations below are valid: 654 655```typescript 656function hi1() { console.log('hi') } 657function hi2(): void { console.log('hi') } 658``` 659 660## Function Scope 661 662Variables and other entities defined in a function are local to the function and cannot be accessed from the outside. 663 664If the name of a variable defined in the function is equal to the name of an entity in the outer scope, then the local definition shadows the outer entity. 665 666## Function Calls 667 668Calling a function actually leads to the execution of its body, while the arguments of the call are assigned to the function parameters. 669 670If the function is defined as follows: 671 672```typescript 673function join(x: string, y: string): string { 674 let z: string = `${x} ${y}` 675 return z 676} 677``` 678 679then it is called with two arguments of the type `string`: 680 681```typescript 682let x = join('hello', 'world') 683console.log(x) 684``` 685 686## Function Types 687 688Function types are commonly used as follows to define callbacks: 689 690```typescript 691type trigFunc = (x: number) => number // this is a function type 692 693function do_action(f: trigFunc) { 694 f(3.141592653589) // call the function 695} 696 697do_action(Math.sin) // pass the function as the parameter 698``` 699 700## Arrow Functions or Lambdas 701 702A function can be defined as an arrow function, for example: 703 704```typescript 705let sum = (x: number, y: number): number => { 706 return x + y 707} 708``` 709 710An arrow function return type can be omitted; in such case, it is inferred from the function body. 711 712An expression can be specified as an arrow function to make the notation shorter, i.e., the following two notations are equivalent: 713 714```typescript 715let sum1 = (x: number, y: number) => { return x + y } 716let sum2 = (x: number, y: number) => x + y 717``` 718 719## Closure 720 721An arrow function is usually defined inside another function. As an inner function, it can access all variables and functions defined in the outer functions. 722 723To capture the context, an inner function forms a closure of its environment. 724The closure allows accessing such an inner function outside its own environment. 725 726```typescript 727function f(): () => number { 728 let count = 0 729 return (): number => { count++; return count } 730} 731 732let z = f() 733console.log(z()) // output: 1 734console.log(z()) // output: 2 735``` 736 737In the sample above, the arrow function closure captures the `count` variable. 738 739## Function Overload Signatures 740 741A function can be specified to be called in different ways by writing overload signatures. To do so, several functions' headers that have the same name but different signatures are written and immediately followed by the single implementation function. 742 743```typescript 744function foo(): void; /* 1st signature */ 745function foo(x: string): void; /* 2nd signature */ 746function foo(x?: string): void { /* Implementation signature */ 747 console.log(x) 748} 749 750foo() // ok, 1st signature is used 751foo('aa') // ok, 2nd signature is used 752``` 753 754An error occurs if two overload signatures have identical parameter lists. 755 756# Classes 757 758A class declaration introduces a new type and defines its fields, methods and constructors. 759 760In the following example, class `Person` is defined, which has fields **name** and **surname**, constructor, and a method `fullName`: 761 762```typescript 763class Person { 764 name: string = '' 765 surname: string = '' 766 constructor (n: string, sn: string) { 767 this.name = n 768 this.surname = sn 769 } 770 fullName(): string { 771 return this.name + ' ' + this.surname 772 } 773} 774``` 775 776After the class is defined, its instances can be created by using the keyword `new`: 777 778```typescript 779let p = new Person('John', 'Smith') 780console.log(p.fullName()) 781``` 782 783or an instance can be created by using object literals: 784 785```typescript 786class Point { 787 x: number = 0 788 y: number = 0 789} 790let p: Point = {x: 42, y: 42} 791``` 792 793## Fields 794 795A field is a variable of some type that is declared directly in a class. 796 797Classes may have instance fields, static fields or both. 798 799### Instance Fields 800 801Instance fields exist on every instance of a class. Each instance has its own set of instance fields. 802 803An instance of the class is used to access an instance field. 804 805```typescript 806class Person { 807 name: string = '' 808 age: number = 0 809 constructor(n: string, a: number) { 810 this.name = n 811 this.age = a 812 } 813 814 GetName(): string { 815 return this.name 816 } 817} 818 819let p1 = new Person('Alice', 25) 820console.log(p1.name) 821let p2 = new Person('Bob', 28) 822console.log(p2.GetName()) 823``` 824 825```typescript 826p1.name 827this.name 828``` 829 830### Static Fields 831 832The keyword `static` is used to declare a field as static. Static fields belong to the class itself, and all instances of the class share one static field. 833 834The class name is used to access a static field: 835 836```typescript 837class Person { 838 static numberOfPersons = 0 839 constructor() { 840 // ... 841 Person.numberOfPersons++ 842 // ... 843 } 844} 845 846console.log(Person.numberOfPersons) 847``` 848 849### Field Initializers 850 851ArkTS requires that all fields are explicitly initialized with some values 852either when the field is declared or in the `constructor`. This is similar 853to `strictPropertyInitialization` mode of the standard TypeScript. Such behavior 854is enforced to minimize the number of unexpected runtime errors and achieve 855better performance. 856 857The following code (invalid in ArkTS) is error-prone: 858 859```typescript 860class Person { 861 name: string // The compiler automatically sets to undefined 862 863 setName(n:string): void { 864 this.name = n 865 } 866 867 getName(): string { 868 // Return type "string" hides from the developers the fact 869 // that name can be undefined. The most correct would be 870 // to write the return type as "string | undefined". By doing so 871 // we tell the users of our API about all possible return values. 872 return this.name 873 } 874} 875 876let jack = new Person() 877// Let's assume that the developer forgets to call setName: 878// jack.setName('Jack') 879console.log(jack.getName().length); // runtime exception: name is undefined 880``` 881 882Here is how it should look in ArkTS: 883 884```typescript 885class Person { 886 name: string = '' // The field is always defined 887 888 setName(n:string): void { 889 this.name = n 890 } 891 892 // The type is always string, no other "hidden options". 893 getName(): string { 894 return this.name 895 } 896} 897 898let jack = new Person() 899// Let's assume that the developer forgets to call setName: 900// jack.setName('Jack') 901console.log(jack.getName().length); // 0, no runtime error 902``` 903 904And here how our code behaves if the field `name` can be `undefined` 905 906```typescript 907class Person { 908 name ?: string // The field may be undefined, great 909 // More explicit syntax may also be used: 910 // name: string | undefined = undefined 911 912 setName(n:string): void { 913 this.name = n 914 } 915 916 // Compile-time error: 917 // name can be "undefined", so we cannot say to those who use this API 918 // that it returns only strings: 919 getNameWrong(): string { 920 return this.name 921 } 922 923 getName(): string | undefined { // Return type matches the type of name 924 return this.name 925 } 926} 927 928let jack = new Person() 929// Let's assume that the developer forgets to call setName: 930// jack.setName('Jack') 931 932// Compile-time(!) error: Compiler suspects that we 933// may possibly access something undefined and won't build the code: 934console.log(jack.getName().length); // The code won't build and run 935 936console.log(jack.getName()?.length); // Builds ok, no runtime error 937``` 938 939### Getters and Setters 940 941Setters and getters can be used to provide controlled access to object properties. 942 943In the following example, a setter is used to forbid setting invalid values of the 'age' property: 944 945```typescript 946class Person { 947 name: string = '' 948 private _age: number = 0 949 get age(): number { return this._age } 950 set age(x: number) { 951 if (x < 0) { 952 throw Error('Invalid age argument') 953 } 954 this._age = x 955 } 956} 957 958let p = new Person() 959console.log (p.age) // 0 will be printed out 960p.age = -42 // Error will be thrown as an attempt to set incorrect age 961``` 962 963A class can define a getter, a setter or both. 964 965## Methods 966 967A method is a function that belongs to a class. 968A class can define instance methods, static methods or both. 969A static method belongs to the class itself, and can have access to static fields only. 970A `while` instance method has access to both static (class) fields and instance fields including private ones of its class. 971 972### Instance Methods 973 974The example below illustrates how instanced methods work. 975The `calculateArea` method calculates the area of a rectangle by multiplying the height by the width: 976 977```typescript 978class Rectangle { 979 private height: number = 0 980 private width: number = 0 981 constructor(height: number, width: number) { 982 // ... 983 } 984 calculateArea(): number { 985 return this.height * this.width; 986 } 987} 988``` 989 990To use an instance method, it must be called on an instance of the class: 991 992```typescript 993let square = new Rectangle(10, 10) 994console.log(square.calculateArea()) // output: 100 995``` 996 997### Static Methods 998 999The keyword `static` is used to declare a method as static. Static methods belong to the class itself and have access to static fields only. 1000A static method defines a common behavior of the class as a whole. 1001All instances have access to static methods. 1002 1003The class name is used to call a static method: 1004 1005```typescript 1006class Cl { 1007 static staticMethod(): string { 1008 return 'this is a static method.' 1009 } 1010} 1011console.log(Cl.staticMethod()) 1012``` 1013 1014### Inheritance 1015 1016A class can extend another class. 1017The class that is being extended by another class is called ‘*base class*’, 1018‘parent class’, or ‘superclass’. 1019The class that extends another class is called ‘*extended class*’, ‘derived 1020class’ or ‘subclass’. 1021 1022An extended class can implement several interfaces by using the 1023following syntax: 1024 1025```typescript 1026class [extends BaseClassName] [implements listOfInterfaces] { 1027 // ... 1028} 1029``` 1030 1031An extended class inherits fields and methods, but not constructors from 1032the base class, and can add its own fields and methods, as well as override 1033methods defined by the base class. 1034 1035Example: 1036 1037```typescript 1038class Person { 1039 name: string = '' 1040 private _age = 0 1041 get age(): number { 1042 return this._age 1043 } 1044} 1045class Employee extends Person { 1046 salary: number = 0 1047 calculateTaxes(): number { 1048 return this.salary * 0.42 1049 } 1050} 1051``` 1052 1053A class containing the `implements` clause must implement all methods defined in all listed interfaces, except the methods defined with default implementation. 1054 1055```typescript 1056interface DateInterface { 1057 now(): string; 1058} 1059class MyDate implements DateInterface { 1060 now(): string { 1061 // implementation is here 1062 return 'now is now' 1063 } 1064} 1065``` 1066 1067### Access to Super 1068 1069The keyword `super` can be used to access instance fields, instance methods and constructors from the super class. 1070 1071It is often used to extend basic functionality of subclass with the required behavior taken from the super class: 1072 1073```typescript 1074class Rectangle { 1075 protected height: number = 0 1076 protected width: number = 0 1077 1078 constructor (h: number, w: number) { 1079 this.height = h 1080 this.width = w 1081 } 1082 1083 draw() { 1084 /* draw bounds */ 1085 } 1086} 1087class FilledRectangle extends Rectangle { 1088 color = '' 1089 constructor (h: number, w: number, c: string) { 1090 super(h, w) // call of super constructor 1091 this.color = c 1092 } 1093 1094 override draw() { 1095 super.draw() // call of super methods 1096 // super.height - can be used here 1097 /* fill rectangle */ 1098 } 1099} 1100``` 1101 1102### Override Methods 1103 1104A subclass can override implementation of a method defined in its superclass. 1105An overridden method can be marked with the keyword `override` to improve readability. 1106An overridden method must have the same types of parameters, and same or derived return type as the original method. 1107 1108```typescript 1109class Rectangle { 1110 // ... 1111 area(): number { 1112 // implementation 1113 return 0 1114 } 1115} 1116class Square extends Rectangle { 1117 private side: number = 0 1118 override area(): number { 1119 return this.side * this.side 1120 } 1121} 1122``` 1123 1124### Method Overload Signatures 1125 1126A method can be specified to be called in different ways by writing overload signatures. To do so, several method headers that have the same name but different signatures are written and immediately followed by the single implementation method. 1127 1128```typescript 1129class C { 1130 foo(): void; /* 1st signature */ 1131 foo(x: string): void; /* 2nd signature */ 1132 foo(x?: string): void { /* implementation signature */ 1133 console.log(x) 1134 } 1135} 1136let c = new C() 1137c.foo() // ok, 1st signature is used 1138c.foo('aa') // ok, 2nd signature is used 1139``` 1140 1141An error occurs if two overload signatures have the same name and identical parameter lists. 1142 1143## Constructors 1144 1145A class declaration may contain a constructor that is used to initialize object state. 1146 1147A constructor is defined as follows: 1148 1149```typescript 1150constructor ([parameters]) { 1151 // ... 1152} 1153``` 1154 1155If no constructor is defined, then a default constructor with an empty parameter list is created automatically, for example: 1156 1157```typescript 1158class Point { 1159 x: number = 0 1160 y: number = 0 1161} 1162let p = new Point() 1163``` 1164 1165In this case the default constructor fills the instance fields with default values for the field types. 1166 1167### Constructors in Derived Class 1168 1169The first statement of a constructor body can use the keyword `super` to explicitly call a constructor of the direct superclass. 1170 1171```typescript 1172class Rectangle { 1173 constructor(width: number, height: number) { 1174 // ... 1175 } 1176} 1177class Square extends Rectangle { 1178 constructor(side: number) { 1179 super(side, side) 1180 } 1181} 1182``` 1183 1184If a constructor body does not begin with such an explicit call of a superclass constructor, then the constructor body implicitly begins with a superclass constructor call `super()`. 1185 1186### Constructor Overload Signatures 1187 1188A constructor can be specified to be called in different ways by writing overload signatures. To do so, several constructor headers that have the same name but different signatures are written and immediately followed by the single implementation constructor. 1189 1190```typescript 1191class C { 1192 constructor() /* 1st signature */ 1193 constructor(x: string) /* 2nd signature */ 1194 constructor(x?: string) { /* Implementation signature */ 1195 console.log(x) 1196 } 1197} 1198let c1 = new C() // ok, 1st signature is used 1199let c2 = new C('abc') // ok, 2nd signature is used 1200``` 1201 1202An error occurs if two overload signatures have the same name and identical parameter lists. 1203 1204## Visibility Modifiers 1205 1206Both methods and properties of a class can have visibility modifiers. 1207 1208There are several visibility modifiers: 1209 1210- `private`, 1211- `protected`, 1212- `public`. 1213 1214The default visibility is `public`. 1215 1216### Public Visibility 1217 1218The `public` members (fields, methods, constructors) of a class are visible in any part of the program, where their class is visible. 1219 1220### Private Visibility 1221 1222A `private` member cannot be accessed outside the class it is declared in. 1223Example: 1224 1225```typescript 1226class C { 1227 public x: string = '' 1228 private y: string = '' 1229 set_y (new_y: string) { 1230 this.y = new_y // ok, as y is accessible within the class itself 1231 } 1232} 1233let c = new C() 1234c.x = 'a' // ok, the field is public 1235c.y = 'b' // compile-time error: 'y' is not visible 1236``` 1237 1238### Protected Visibility 1239 1240The modifier `protected` acts much like the modifier `private`, but the `protected` members are also accessible in derived classes. 1241Example: 1242 1243```typescript 1244class Base { 1245 protected x: string = '' 1246 private y: string = '' 1247} 1248class Derived extends Base { 1249 foo() { 1250 this.x = 'a' // ok, access to protected member 1251 this.y = 'b' // compile-time error, 'y' is not visible, as it is private 1252 } 1253} 1254``` 1255 1256## Object Literals 1257 1258An object literal is an expression that can be used to create a class instance and provide some initial values. It can be used instead of the expression `new` as it is more convenient in some cases. 1259 1260A class composite is written as a comma-separated list of name-value pairs enclosed in '{' and '}'. 1261 1262```typescript 1263class C { 1264 n: number = 0 1265 s: string = '' 1266} 1267 1268let c: C = {n: 42, s: 'foo'} 1269``` 1270 1271Due to the static typing of the ArkTS, object literals can be used in a context where the class or interface type of the object literal can be inferred as in the example above. Other valid cases are illustrated below: 1272 1273```typescript 1274class C { 1275 n: number = 0 1276 s: string = '' 1277} 1278 1279function foo(c: C) {} 1280 1281let c: C 1282 1283c = {n: 42, s: 'foo'} // type of the variable is used 1284foo({n: 42, s: 'foo'}) // type of the parameter is used 1285 1286function bar(): C { 1287 return {n: 42, s: 'foo'} // return type is used 1288} 1289``` 1290 1291The type of an array element or of a class field can also be used: 1292 1293```typescript 1294class C { 1295 n: number = 0 1296 s: string = '' 1297} 1298let cc: C[] = [{n: 1, s: 'a'}, {n: 2, s: 'b'}] 1299``` 1300 1301### Object Literals of Record Type 1302 1303The generic Record<K, V> type is used to map the properties of a type (Key type) to another type (Value type). 1304 1305A special form of object literal is used to initialize the value of such type: 1306 1307```typescript 1308let map: Record<string, number> = { 1309 'John': 25, 1310 'Mary': 21, 1311} 1312 1313console.log(map['John']) // prints 25 1314``` 1315 1316The K type can be either string or number, while V can be any type. 1317 1318```typescript 1319interface PersonInfo { 1320 age: number 1321 salary: number 1322} 1323let map: Record<string, PersonInfo> = { 1324 'John': { age: 25, salary: 10}, 1325 'Mary': { age: 21, salary: 20} 1326} 1327``` 1328 1329# Interfaces 1330 1331An interface declaration introduces a new type. Interfaces are a common way of defining contracts between various part of codes. 1332 1333Interfaces are used to write polymorphic code, which can be applied to any class instances that implement a particular interface. 1334 1335An interface usually contains properties and method headers. 1336 1337Examples: 1338 1339```typescript 1340interface Style { 1341 color: string // property 1342} 1343interface Area { 1344 calculateArea(): number // method header 1345 someMethod(): void; // method header 1346} 1347``` 1348 1349Examples of a class implementing an interface: 1350 1351```typescript 1352// Interface: 1353interface Area { 1354 calculateArea(): number // method header 1355 someMethod(): void; // method header 1356} 1357 1358// Implementation: 1359class Rectangle implements Area { 1360 private width: number = 0 1361 private height: number = 0 1362 someMethod(): void { 1363 console.log('someMethod called') 1364 } 1365 calculateArea(): number { 1366 this.someMethod() // calls another method and returns result 1367 return this.width * this.height 1368 } 1369} 1370``` 1371 1372## Interface Properties 1373 1374An interface property can be in a form of field, getter, setter, or both getter and setter. 1375 1376A property field is just a shortcut notation of a getter/setter pair, and the following notations are equal: 1377 1378```typescript 1379interface Style { 1380 color: string 1381} 1382 1383interface Style { 1384 get color(): string 1385 set color(x: string) 1386} 1387``` 1388 1389A class that implements an interface may also use a short or a long notation: 1390 1391```typescript 1392interface Style { 1393 color: string 1394} 1395 1396class StyledRectangle implements Style { 1397 color: string = '' 1398} 1399``` 1400 1401The short notation implicitly defines a private field and getter and setter: 1402 1403```typescript 1404interface Style { 1405 color: string 1406} 1407 1408class StyledRectangle implements Style { 1409 private _color: string = '' 1410 get color(): string { return this._color } 1411 set color(x: string) { this._color = x } 1412} 1413``` 1414 1415## Interface Inheritance 1416 1417An interface may extend other interfaces like in the example below: 1418 1419```typescript 1420interface Style { 1421 color: string 1422} 1423 1424interface ExtendedStyle extends Style { 1425 width: number 1426} 1427``` 1428 1429An extended interface contains all properties and methods of the interface it extends, and can also add its own properties and methods. 1430 1431## Interface Visibility Modifiers 1432 1433Properties and methods are `public`. 1434 1435Only methods with default implementation can be defined as `private`. 1436 1437# Generic Types and Functions 1438 1439Generic types and functions allow creating the code capable to work over a variety of types rather than a single type. 1440 1441## Generic Classes and Interfaces 1442 1443A class and an interface can be defined as generics, adding parameters to the type definition, like the type parameter `Element` in the following example: 1444 1445```typescript 1446class Stack<Element> { 1447 public pop(): Element { 1448 // ... 1449 } 1450 public push(e: Element): void { 1451 // ... 1452 } 1453} 1454``` 1455 1456To use type Stack, the type argument must be specified for each type parameter: 1457 1458```typescript 1459let s = new Stack<string> 1460s.push('hello') 1461``` 1462 1463Compiler ensures type safety while working with generic types and functions. 1464See below: 1465 1466```typescript 1467let s = new Stack<string> 1468s.push(55) /* That will be a compile-time error as 55 is not compatible 1469 with type string */ 1470``` 1471 1472## Generic Constraints 1473 1474Type parameters of generic types can be bounded. For example, the `Key` type parameter in the `HashMap<Key, Value>` container must have a hash method, that is, it must be hashable. 1475 1476```typescript 1477interface Hashable { 1478 hash(): number 1479} 1480class HasMap<Key extends Hashable, Value> { 1481 public set(k: Key, v: Value) { 1482 let h = k.hash() 1483 // ... other code ... 1484 } 1485} 1486``` 1487 1488In the above example, the `Key` type extends `Hashable`, and all methods of `Hashable` interface can be called for keys. 1489 1490## Generic Functions 1491 1492Use a generic function to create a more universal code. Consider a function that returns the last element of the array: 1493 1494```typescript 1495function last(x: number[]): number { 1496 return x[x.length - 1] 1497} 1498console.log(last([1, 2, 3])) // output: 3 1499``` 1500 1501If the same function needs to be defined for any array, then define it as a generic with a type parameter: 1502 1503```typescript 1504function last<T>(x: T[]): T { 1505 return x[x.length - 1] 1506} 1507``` 1508 1509Now, the function can be used with any array. 1510 1511In a function call, type argument can be set explicitly or implicitly: 1512 1513```typescript 1514// Explicit type argument 1515console.log(last<string>(['aa', 'bb'])) 1516console.log(last<number>([1, 2, 3])) 1517 1518// Implicit type argument: 1519// Compiler understands the type argument based on the type of the call arguments 1520console.log(last([1, 2, 3])) 1521``` 1522 1523## Generic Defaults 1524 1525Type parameters of generic types can have defaults. It allows using just the generic type name instead of specifying the actual type arguments. 1526The example below illustrates this for both classes and functions. 1527 1528```typescript 1529class SomeType {} 1530interface Interface <T1 = SomeType> { } 1531class Base <T2 = SomeType> { } 1532class Derived1 extends Base implements Interface { } 1533// Derived1 is semantically equivalent to Derived2 1534class Derived2 extends Base<SomeType> implements Interface<SomeType> { } 1535 1536function foo<T = number>(): T { 1537 // ... 1538} 1539foo() 1540// such function is semantically equivalent to the call below 1541foo<number>() 1542``` 1543 1544# Null Safety 1545 1546All types in ArkTS by default are non-nullable, so the value of a type cannot be null. 1547It is similar to TypeScript behavior in strict null checking mode (`strictNullChecks`), but the rules are stricter. 1548 1549In the example below, all lines cause a compile-time error: 1550 1551```typescript 1552let x: number = null // Compile-time error 1553let y: string = null // Compile-time error 1554let z: number[] = null // Compile-time error 1555``` 1556 1557A variable that can have a null value is defined with a union type `T | null`. 1558 1559```typescript 1560let x: number | null = null 1561x = 1 // ok 1562x = null // ok 1563if (x != null) { /* do something */ } 1564``` 1565 1566## Non-Null Assertion Operator 1567 1568A postfix operator `!` can be used to assert that its operand is non-null. 1569 1570If applied to a null value, the operator throws an error. Otherwise, the type of the value is changed from `T | null` to `T`: 1571 1572```typescript 1573let x: number | null = 1 1574let y: number 1575y = x + 1 // compile time error: cannot add to a nullable value 1576y = x! + 1 // ok 1577``` 1578 1579## Null-Coalescing Operator 1580 1581The null-coalescing binary operator `??` checks whether the evaluation of the left-hand-side expression is equal to null. 1582If it is, then the result of the expression is the right-hand-side expression; otherwise, it is the left-hand-side expression. 1583 1584In other words, `a ?? b` equals the ternary operator `a != null ? a : b`. 1585 1586In the following example, the method `getNick` returns a nickname if it is set; otherwise, an empty string is returned: 1587 1588```typescript 1589class Person { 1590 // ... 1591 nick: string | null = null 1592 getNick(): string { 1593 return this.nick ?? '' 1594 } 1595} 1596``` 1597 1598## Optional Chaining 1599 1600Optional chaining operator `?.` allows writing code where the evaluation stops at an expression that is partially evaluated to `null` or `undefined`. 1601 1602```typescript 1603class Person { 1604 nick: string | null = null 1605 spouse?: Person 1606 1607 setSpouse(spouse: Person): void { 1608 this.spouse = spouse 1609 } 1610 1611 getSpouseNick(): string | null | undefined { 1612 return this.spouse?.nick 1613 } 1614 1615 constructor(nick: string) { 1616 this.nick = nick 1617 this.spouse = undefined 1618 } 1619} 1620``` 1621 1622**Note**: The return type of `getSpouseNick` must be `string | null | undefined`, as the method can return `null` or `undefined`. 1623 1624An optional chain can be of any length and contain any number of `?.` operators. 1625 1626In the following sample, the output is a person's spouse nickname if that person has a spouse, and the spouse has a nickname. 1627 1628Otherwise, the output is `undefined`: 1629 1630```typescript 1631class Person { 1632 nick: string | null = null 1633 spouse?: Person 1634 1635 constructor(nick: string) { 1636 this.nick = nick 1637 this.spouse = undefined 1638 } 1639} 1640 1641let p: Person = new Person('Alice') 1642console.log(p.spouse?.nick) // print: undefined 1643``` 1644 1645# Modules 1646 1647Programs are organized as sets of compilation units or modules. 1648 1649Each module creates its own scope, i.e., any declarations (variables, functions, classes, etc.) declared in the module are not visible outside that module unless they are explicitly exported. 1650 1651Conversely, a variable, function, class, interface, etc. exported from another module must first be imported to a module. 1652 1653## Export 1654 1655A top-level declaration can be exported by using the keyword `export`. 1656 1657A declared name that is not exported is considered private and can be used only in the module where it is declared. 1658 1659```typescript 1660export class Point { 1661 x: number = 0 1662 y: number = 0 1663 constructor(x: number, y: number) { 1664 this.x = x 1665 this.y = y 1666 } 1667} 1668export let Origin = new Point(0, 0) 1669export function Distance(p1: Point, p2: Point): number { 1670 return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)) 1671} 1672``` 1673 1674## Import 1675 1676Import declarations are used to import entities exported from other modules and provide their bindings in the current module. 1677An import declaration consists of two parts: 1678 1679* Import path that determines the module to import from. 1680* Import bindings that define the set of usable entities in the imported module, and the form of use (i.e., qualified or unqualified use). 1681 1682Import bindings may have several forms. 1683 1684Let's assume a module has the path './utils' and export entities 'X' and 'Y'. 1685 1686An import binding of the form `* as A` binds the name 'A', and all entities exported from the module defined by the import path can be accessed by using 1687the qualified name `A.name`: 1688 1689```typescript 1690import * as Utils from './utils' 1691Utils.X // denotes X from Utils 1692Utils.Y // denotes Y from Utils 1693``` 1694 1695An import binding of the form `{ ident1, ..., identN }` binds an exported entity with a specified name, which can be used as a simple name: 1696 1697```typescript 1698import { X, Y } from './utils' 1699X // denotes X from Utils 1700Y // denotes Y from Utils 1701``` 1702 1703If a list of identifiers contains aliasing of the form `ident as alias`, then entity `ident` is bound under the name `alias`: 1704 1705```typescript 1706import { X as Z, Y } from './utils' 1707Z // denotes X from Utils 1708Y // denotes Y from Utils 1709X // Compile-time error: 'X' is not visible 1710``` 1711 1712## Top-Level Statements 1713 1714A module can contain any statements at the module level, except `return` ones. 1715 1716If a module contains a `main` function (program entry point), then top-level statements of the module are executed immediately before the body of this function. 1717Otherwise, they are executed before execution of any other function of the module. 1718 1719## Program Entry Point 1720 1721An entry point of a program (application) is the top-level `main` function. 1722The `main` function must have either an empty parameter list or a single parameter of `string[]` type. 1723 1724```typescript 1725function main() { 1726 console.log('this is the program entry') 1727} 1728``` 1729 1730# Support for ArkUI 1731 1732This section demonstrates mechanisms that ArkTS provides for creating graphical user interface (GUI) programs. The section is based on the ArkUI declarative framework. ArkUI provides a set of extensions of the standard TypeScript to declaratively describe the GUI of the applications and the interaction between the GUI components. 1733 1734## ArkUI Example 1735 1736The [Example](arkts-mvvm.md#example) provides a complete ArkUI-based application as an illustration of GUI programming capabilities. 1737 1738For more details of the ArkUI features, refer to the ArkUI [tutorial](arkts-get-started.md). 1739