1# ArkTS Migration Background 2 3<!--Kit: ArkTS--> 4<!--Subsystem: ArkCompiler--> 5<!--Owner: @Fouckttt1--> 6<!--Designer: @qyhuo32--> 7<!--Tester: @kirl75; @zsw_zhushiwei--> 8<!--Adviser: @zhang_yixin13--> 9 10Building on the basic syntax of TypeScript (TS), ArkTS further strengthens static checks and analysis. This allows for the detection of more errors during development, thereby enhancing program stability and running performance. This topic explains why it makes sense to migrate from the standard TS to ArkTS. 11 12## Program Stability 13 14Dynamically typed languages like JavaScript (JS) can improve development efficiency, but they are prone to unexpected runtime errors. For example, an unchecked **undefined** value may cause program breakdown. If such issues can be detected during development, the program stability can be significantly improved. TS allows to annotate the code with types, which makes errors more detectable for the compiler. However, its non-forcible type system still has limitations. For example, variables without type annotations may hinder complete compilation check. ArkTS overcomes this drawback by forcibly using a static type system and implementing a stricter type check, thereby minimizing runtime errors. 15 16The following case demonstrates how we can improve stability and correctness of our code by enforcing stricter type checking in ArkTS. 17 18 19**Explicit Initialization of Fields for Better Stability** 20 21ArkTS requires that all fields are explicitly initialized with some values either when the field is declared or in **constructor**. This is similar to **strictPropertyInitialization** mode of the standard TS. Let's look at the following TS code: 22 23```typescript 24class Person { 25 name: string; // undefined 26 27 setName(n: string): void { 28 this.name = n; 29 } 30 31 getName(): string { 32 // Return type "string" hides from the developers the fact that name can be undefined. 33 // The most correct would be to write the return type as "string | undefined". By doing so, we tell the users of our API about all possible return values. 34 return this.name; 35 } 36} 37 38let buddy = new Person() 39// Let's assume that the developer forgets to call buddy.setName("John"). 40buddy.getName().length; // Runtime exception: name is undefined. 41``` 42 43ArkTS requires explicit initialization. The code is as follows: 44 45```typescript 46class Person { 47 name: string = ''; 48 49 setName(n: string): void { 50 this.name = n; 51 } 52 53 // The type is string in all cases, null and undefined are impossible. 54 getName(): string { 55 return this.name; 56 } 57} 58 59let buddy = new Person() 60// Let's assume that the developer forgets to call buddy.setName("John"). 61buddy.getName().length; // 0, no runtime error 62``` 63 64If **name** can be **undefined**, its type must be accurately annotated in the code. 65 66```typescript 67class Person { 68 name?: string; // The field may be undefined. 69 70 setName(n: string): void { 71 this.name = n; 72 } 73 74 // Compile-time error: name can be "undefined", so the return type of this API cannot be marked as "string". 75 getNameWrong(): string { 76 return this.name; 77 } 78 79 getName(): string | undefined { // Return type matches the type of name. 80 return this.name; 81 } 82} 83 84let buddy = new Person() 85// Let's assume that the developer forgets to call buddy.setName("John"). 86 87// Compile-time error: Compiler suspects that we may possibly access something undefined and will not build the code: 88buddy.getName().length; // The code will not build and run. 89 90buddy.getName()?.length; // Successful builds and no runtime error. 91``` 92 93## Program Performance 94 95To ensure program correctness, dynamically typed languages have to check object types at runtime. Back to our example, the undefined property cannot be read in JS. But the only way to check if some value is undefined is to perform a runtime check, and all JS engines will perform as follows: If the value is not **undefined**, the property is read, otherwise an exception is thrown. Modern engines can optimize such checks greatly, but these checks cannot be eliminated completely, which slows down the program. Since the standard TS compiles to JS, the code written in TS has the same issues as described above. ArkTS addresses this problem. It enforces a static type check and compiles the program to Ark bytecode instead of JS, thus speeding up the execution and making it easier to optimize the code even further. 96 97 98**Null Safety** 99 100```typescript 101function notify(who: string, what: string) { 102 console.info(`Dear ${who}, a message for you: ${what}`); 103} 104 105notify('Jack', 'You look great today'); 106``` 107 108In most cases, the **notify** function will take two **string** variables as an input and produces a new string. However, what if we pass some "special" values to the function, for example **notify(null, undefined)**? 109The program will continue to work, the output will be as expected (**Dear null, a message for you: undefined**), so from the first glance everything is fine. But please note that the engine that runs our code should always check for such special cases to ensure correct behavior. In pseudocode, something like this happens: 110 111```typescript 112function __internal_tostring(s: any): string { 113 if (typeof s === 'string') 114 return s; 115 if (s === undefined) 116 return 'undefined'; 117 if (s === null) 118 return 'null'; 119 // ... 120} 121``` 122 123Now imagine that the **notify** function is a part of some complex heavy-loaded system which sends real notifications instead of just writing to the log. In this scenario, executing all these checks from the **__internal_tostring** function may turn into a performance problem. 124 125But what if we could somehow guarantee to our execution engine that the only values that are passed to the **notify** function are "real" strings, but not some "special" values like **null** or **undefined**? In this case, checks like **__internal_tostring** become redundant because when we execute the program we can ensure that there will be no corner cases. For this particular case, this mechanism would be called "null-safety", which guarantees that **null** is not a valid value of the string type. If ArkTS has such feature, any code that does not match the type will be intercepted at the compilation stage and cannot be compiled. 126 127```typescript 128function notify(who: string, what: string) { 129 console.info(`Dear ${who}, a message for you: ${what}`); 130} 131 132notify('Jack', 'You look great today'); 133notify(null, undefined); // Compile-time error 134``` 135 136In TS such behavior can be turned on by a special compiler flag called **strictNullChecks**. But since the standard TS is compiled to JS, which does not have such feature, strict null checks work only in compile-time, for better type checking. However, ArkTS considers null-safety a very important feature from both stability and performance points of view. That is why it enforces strict null check and the example above always produces compile-time errors. In exchange, such code provides ArkTS engine with more information and guarantees about possible types, optimizing performance. 137 138 139## .ets Code Compatibility 140 141Prior to API version 10, ArkTS (.ets file) completely adopted the syntax of standard TS. Since API version 10, the ArkTS syntax rules are clearly defined. In addition, the SDK adds the ArkTS syntax validation for .ets files to the compilation process, and prompts you to adapt to the new ArkTS syntax through warnings or errors. 142 143Syntax issues are classified as warning or error, depending on the **compatibleSdkVersion** of the project: 144 145 - In standard mode, where the value of **compatibleSdkVersion** is greater than or equal to 10, syntax issues are reported as errors and will block the compilation process. The compilation can be successful only after the ArkTS syntax is fully adapted. 146 - In compatible mode, where the value of **compatibleSdkVersion** is smaller than 10, syntax issues are reported as warnings and will not block the compilation process. 147 148## Interaction with TS/JS 149 150ArkTS supports efficient interoperability with TS/JS. In the current version, ArkTS is compatible with dynamic object semantics. In the scenario of interaction with TS/JS, when data and objects of TS/JS are used as those of ArkTS, the static type check of ArkTS may be bypassed, causing unexpected behavior or extra overhead. 151 152```typescript 153// lib.ts 154export class C { 155 v: string; // Compile-time error reported: Property 'v' has no initializer 156} 157 158export let c = new C() 159 160// app.ets 161import { C, c } from './lib'; 162 163function foo(c: C) { 164 c.v.length; 165} 166 167foo(c); 168``` 169 170## ArkCompiler Runtime Compatibility with TS/JS 171 172The OpenHarmony SDK of API version 11 uses TypeScript 4.9.5, with the **target** field of **es2017**. In the application, you can use the syntax of ECMA2017+ for TS/JS development. 173 174**Application Environment Restrictions** 175 1761. Force the use of strict mode (use strict). 1772. Prohibit the use of **eval()**. 1783. Prohibit the use of **with() {}**. 1794. Prohibit creating functions with strings as code. 1805. Prohibit circular dependency. 181 182 Example of circular dependency: 183 ```typescript 184 // bar.ets 185 import {v} from './foo'; // bar.ets depends on foo.ets. 186 export let u = 0; 187 188 // foo.ets 189 import {u} from './bar'; // foo.ets depends on bar.ets reversely. 190 export let v = 0; 191 192 // Application loading fails. 193 ``` 194 195**Differences from Standard TS/JS** 196 197In standard TS/JS, the number format of JSON requires that the decimal point must be followed by a number. For example, scientific notation such as **2.e3** is not allowed and will throw **SyntaxError**. In the ArkCompiler Runtime, this type of scientific notation is allowed. 198