• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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