1# 从TypeScript到ArkTS的适配规则
2
3ArkTS通过规范约束了TypeScript(简称TS)中过于灵活而影响开发正确性或者给运行时带来不必要额外开销的特性。本文罗列了所有在ArkTS中限制的TS特性,并提供了重构代码的建议。ArkTS保留了TS大部分的语法特性,对于本文中没有约束的TS特性,则说明ArkTS完全支持它们。例如:ArkTS支持自定义装饰器,语法上和TS一致。按照本文提供的约束进行代码重构后的代码仍为合法有效的TS代码。
4
5**示例**
6
7包含关键字`var`的原始TypeScript代码:
8
9```typescript
10function addTen(x: number): number {
11  var ten = 10;
12  return x + ten;
13}
14```
15
16重构后的代码:
17
18```typescript
19function addTen(x: number): number {
20  let ten = 10;
21  return x + ten;
22}
23```
24
25**级别**
26
27约束分为两个级别:错误、警告。
28
29- **错误**: 必须要遵从的约束。如果不遵从该约束,将会导致程序编译失败。
30- **警告**: 推荐遵从的约束。尽管现在违反该约束不会影响编译流程,但是在将来,违反该约束可能将会导致程序编译失败。
31
32**不支持的特性**
33
34目前,不支持的特性主要包括:
35
36- 与降低运行时性能的动态类型相关的特性。
37- 需要编译器额外支持从而导致项目构建时间增加的特性。
38
39根据开发者的反馈以及更多实际场景的数据,我们将来可能进一步**缩小**不支持特性的范围。
40
41## 概述
42
43本节罗列了ArkTS不支持或部分支持的TypeScript特性。完整的列表以及详细的代码示例和重构建议,请参考[约束说明](#约束说明)。更多案例请参考[适配指导案例](arkts-more-cases.md)。
44
45### 强制使用静态类型
46
47静态类型是ArkTS最重要的特性之一。如果程序采用静态类型,即所有类型在编译时都是已知的,那么开发者就能够容易理解代码中使用了哪些数据结构。同时,由于所有类型在程序实际运行前都是已知的,编译器可以提前验证代码的正确性,从而可以减少运行时的类型检查,有助于提升性能。
48
49基于上述考虑,ArkTS中禁止使用`any`类型。
50
51**示例**
52
53```typescript
54// 不支持:
55let res: any = some_api_function('hello', 'world');
56// `res`是什么?错误代码的数字?字符串?对象?
57// 该如何处理它?
58// 支持:
59class CallResult {
60  public succeeded(): boolean { ... }
61  public errorMessage(): string { ... }
62}
63
64let res: CallResult = some_api_function('hello', 'world');
65if (!res.succeeded()) {
66  console.log('Call failed: ' + res.errorMessage());
67}
68```
69
70`any`类型在TypeScript中并不常见,只有大约1%的TypeScript代码库使用。一些代码检查工具(例如ESLint)也制定一系列规则来禁止使用`any`。因此,虽然禁止`any`将导致代码重构,但重构量很小,有助于整体性能提升。
71
72### 禁止在运行时变更对象布局
73
74为实现最佳性能,ArkTS要求在程序执行期间不能更改对象的布局。换句话说,ArkTS禁止以下行为:
75
76- 向对象中添加新的属性或方法。
77- 从对象中删除已有的属性或方法。
78- 将任意类型的值赋值给对象属性。
79
80TypeScript编译器已经禁止了许多此类操作。然而,有些操作还是有可能绕过编译器的,例如,使用`as any`转换对象的类型,或者在编译TS代码时关闭严格类型检查的配置,或者在代码中通过`@ts-ignore`忽略类型检查。
81
82在ArkTS中,严格类型检查不是可配置项。ArkTS强制进行部分严格类型检查,并通过规范禁止使用`any`类型,禁止在代码中使用`@ts-ignore`。
83
84**示例**
85
86```typescript
87class Point {
88  public x: number = 0
89  public y: number = 0
90
91  constructor(x: number, y: number) {
92    this.x = x;
93    this.y = y;
94  }
95}
96
97// 无法从对象中删除某个属性,从而确保所有Point对象都具有属性x
98let p1 = new Point(1.0, 1.0);
99delete p1.x;           // 在TypeScript和ArkTS中,都会产生编译时错误
100delete (p1 as any).x;  // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
101
102// Point类没有定义命名为z的属性,在程序运行时也无法添加该属性
103let p2 = new Point(2.0, 2.0);
104p2.z = 'Label';           // 在TypeScript和ArkTS中,都会产生编译时错误
105(p2 as any).z = 'Label';   // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
106
107// 类的定义确保了所有Point对象只有属性x和y,并且无法被添加其他属性
108let p3 = new Point(3.0, 3.0);
109let prop = Symbol();      // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
110(p3 as any)[prop] = p3.x; // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
111p3[prop] = p3.x;          // 在TypeScript和ArkTS中,都会产生编译时错误
112
113// 类的定义确保了所有Point对象的属性x和y都具有number类型,因此,无法将其他类型的值赋值给它们
114let p4 = new Point(4.0, 4.0);
115p4.x = 'Hello!';          // 在TypeScript和ArkTS中,都会产生编译时错误
116(p4 as any).x = 'Hello!'; // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
117
118// 使用符合类定义的Point对象:
119function distance(p1: Point, p2: Point): number {
120  return Math.sqrt(
121    (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
122  );
123}
124let p5 = new Point(5.0, 5.0);
125let p6 = new Point(6.0, 6.0);
126console.log('Distance between p5 and p6: ' + distance(p5, p6));
127```
128
129修改对象布局会影响代码的可读性以及运行时性能。从开发者的角度来说,在某处定义类,然后又在其他地方修改实际的对象布局,很容易引起困惑乃至引入错误。此外,这点还需要额外的运行时支持,增加了执行开销。这一点与静态类型的约束也冲突:既然已决定使用显式类型,为什么还需要添加或删除属性呢?
130
131当前,只有少数项目允许在运行时变更对象布局,一些常用的代码检查工具也增加了相应的限制规则。这个约束只会导致少量代码重构,但会提升性能。
132
133### 限制运算符的语义
134
135为获得更好的性能并鼓励开发者编写更清晰的代码,ArkTS限制了一些运算符的语义。详细的语义限制,请参考[约束说明](#约束说明)。
136
137**示例**
138
139```typescript
140// 一元运算符`+`只能作用于数值类型:
141let t = +42;   // 合法运算
142let s = +'42'; // 编译时错误
143```
144
145使用额外的语义重载语言运算符会增加语言规范的复杂度,而且,开发者还被迫牢记所有可能的例外情况及对应的处理规则。在某些情况下,产生一些不必要的运行时开销。
146
147当前只有不到1%的代码库使用该特性。因此,尽管限制运算符的语义需要重构代码,但重构量很小且非常容易操作,并且,通过重构能使代码更清晰、具备更高性能。
148
149### 不支持 structural typing
150
151假设两个不相关的类`T`和`U`拥有相同的`public`API:
152
153```typescript
154class T {
155  public name: string = ''
156
157  public greet(): void {
158    console.log('Hello, ' + this.name);
159  }
160}
161
162class U {
163  public name: string = ''
164
165  public greet(): void {
166    console.log('Greetings, ' + this.name);
167  }
168}
169```
170
171能把类型为`T`的值赋给类型为`U`的变量吗?
172
173```typescript
174let u: U = new T(); // 是否允许?
175```
176
177能把类型为`T`的值传递给接受类型为`U`的参数的函数吗?
178
179```typescript
180function greeter(u: U) {
181  console.log('To ' + u.name);
182  u.greet();
183}
184
185let t: T = new T();
186greeter(t); // 是否允许?
187```
188
189换句话说,我们将采取下面哪种方法呢:
190
191- `T`和`U`没有继承关系或没有`implements`相同的接口,但由于它们具有相同的`public`API,它们“在某种程度上是相等的”,所以上述两个问题的答案都是“是”;
192- `T`和`U`没有继承关系或没有`implements`相同的接口,应当始终被视为完全不同的类型,因此上述两个问题的答案都是“否”。
193
194采用第一种方法的语言支持structural typing,而采用第二种方法的语言则不支持structural typing。目前TypeScript支持structural typing,而ArkTS不支持。
195
196structural typing是否有助于生成清晰、易理解的代码,关于这一点并没有定论。那为什么ArkTS不支持structural typing呢?
197
198因为对structural typing的支持是一个重大的特性,需要在语言规范、编译器和运行时进行大量的考虑和仔细的实现。另外,由于ArkTS使用静态类型,运行时为了支持这个特性需要额外的性能开销。
199
200鉴于此,当前我们还不支持该特性。根据实际场景的需求和反馈,我们后续会重新加以考虑。更多案例和建议请参考[约束说明](#约束说明)。
201
202## 约束说明
203
204### 对象的属性名必须是合法的标识符
205
206**规则:**`arkts-identifiers-as-prop-names`
207
208**级别:错误**
209
210在ArkTS中,对象的属性名不能为数字或字符串。例外:ArkTS支持属性名为字符串字面量和枚举中的字符串值。通过属性名访问类的属性,通过数值索引访问数组元素。
211
212**TypeScript**
213
214```typescript
215var x = { 'name': 'x', 2: '3' };
216
217console.log(x['name']);
218console.log(x[2]);
219```
220
221**ArkTS**
222
223```typescript
224class X {
225  public name: string = ''
226}
227let x: X = { name: 'x' };
228console.log(x.name);
229
230let y = ['a', 'b', 'c'];
231console.log(y[2]);
232
233// 在需要通过非标识符(即不同类型的key)获取数据的场景中,使用Map<Object, some_type>。
234let z = new Map<Object, string>();
235z.set('name', '1');
236z.set(2, '2');
237console.log(z.get('name'));
238console.log(z.get(2));
239
240enum Test {
241  A = 'aaa',
242  B = 'bbb'
243}
244
245let obj: Record<string, number> = {
246  [Test.A]: 1,   // 枚举中的字符串值
247  [Test.B]: 2,   // 枚举中的字符串值
248  ['value']: 3   // 字符串字面量
249}
250```
251
252### 不支持`Symbol()`API
253
254**规则:**`arkts-no-symbol`
255
256**级别:错误**
257
258TypeScript中的`Symbol()`API用于在运行时生成唯一的属性名称。由于该API的常见使用场景在静态类型语言中没有意义,因此,ArkTS不支持`Symbol()`API。在ArkTS中,对象布局在编译时就确定了,且不能在运行时被更改。
259
260ArkTS只支持`Symbol.iterator`。
261
262### 不支持以`#`开头的私有字段
263
264**规则:**`arkts-no-private-identifiers`
265
266**级别:错误**
267
268ArkTS不支持使用`#`符号开头声明的私有字段。改用`private`关键字。
269
270**TypeScript**
271
272```typescript
273class C {
274  #foo: number = 42
275}
276```
277
278**ArkTS**
279
280```typescript
281class C {
282  private foo: number = 42
283}
284```
285
286### 类型、命名空间的命名必须唯一
287
288**规则:**`arkts-unique-names`
289
290**级别:错误**
291
292类型(类、接口、枚举)、命名空间的命名必须唯一,且与其他名称(例如:变量名、函数名)不同。
293
294**TypeScript**
295
296```typescript
297let X: string
298type X = number[] // 类型的别名与变量同名
299```
300
301**ArkTS**
302
303```typescript
304let X: string
305type T = number[] // 为避免名称冲突,此处不允许使用X
306```
307
308### 使用`let`而非`var`
309
310**规则:**`arkts-no-var`
311
312**级别:错误**
313
314`let`关键字可以在块级作用域中声明变量,帮助程序员避免错误。因此,ArkTS不支持`var`,请使用`let`声明变量。
315
316**TypeScript**
317
318```typescript
319function f(shouldInitialize: boolean) {
320  if (shouldInitialize) {
321     var x = 'b';
322  }
323  return x;
324}
325
326console.log(f(true));  // b
327console.log(f(false)); // undefined
328
329let upperLet = 0;
330{
331  var scopedVar = 0;
332  let scopedLet = 0;
333  upperLet = 5;
334}
335scopedVar = 5; // 可见
336scopedLet = 5; // 编译时错误
337```
338
339**ArkTS**
340
341```typescript
342function f(shouldInitialize: boolean): string {
343  let x: string = 'a';
344  if (shouldInitialize) {
345    x = 'b';
346  }
347  return x;
348}
349
350console.log(f(true));  // b
351console.log(f(false)); // a
352
353let upperLet = 0;
354let scopedVar = 0;
355{
356  let scopedLet = 0;
357  upperLet = 5;
358}
359scopedVar = 5;
360scopedLet = 5; //编译时错误
361```
362
363### 使用具体的类型而非`any`或`unknown`
364
365**规则:**`arkts-no-any-unknown`
366
367**级别:错误**
368
369ArkTS不支持`any`和`unknown`类型。显式指定具体类型。
370
371**TypeScript**
372
373```typescript
374let value1: any
375value1 = true;
376value1 = 42;
377
378let value2: unknown
379value2 = true;
380value2 = 42;
381```
382
383**ArkTS**
384
385```typescript
386let value_b: boolean = true; // 或者 let value_b = true
387let value_n: number = 42; // 或者 let value_n = 42
388let value_o1: Object = true;
389let value_o2: Object = 42;
390```
391
392### 使用`class`而非具有call signature的类型
393
394**规则:**`arkts-no-call-signatures`
395
396**级别:错误**
397
398ArkTS不支持对象类型中包含call signature。
399
400**TypeScript**
401
402```typescript
403type DescribableFunction = {
404  description: string
405  (someArg: string): string // call signature
406}
407
408function doSomething(fn: DescribableFunction): void {
409  console.log(fn.description + ' returned ' + fn(''));
410}
411```
412
413**ArkTS**
414
415```typescript
416class DescribableFunction {
417  description: string
418  public invoke(someArg: string): string {
419    return someArg;
420  }
421  constructor() {
422    this.description = 'desc';
423  }
424}
425
426function doSomething(fn: DescribableFunction): void {
427  console.log(fn.description + ' returned ' + fn.invoke(''));
428}
429
430doSomething(new DescribableFunction());
431```
432
433### 使用`class`而非具有构造签名的类型
434
435**规则:**`arkts-no-ctor-signatures-type`
436
437**级别:错误**
438
439ArkTS不支持对象类型中的构造签名。改用类。
440
441**TypeScript**
442
443```typescript
444class SomeObject {}
445
446type SomeConstructor = {
447  new (s: string): SomeObject
448}
449
450function fn(ctor: SomeConstructor) {
451  return new ctor('hello');
452}
453```
454
455**ArkTS**
456
457```typescript
458class SomeObject {
459  public f: string
460  constructor (s: string) {
461    this.f = s;
462  }
463}
464
465function fn(s: string): SomeObject {
466  return new SomeObject(s);
467}
468```
469
470### 仅支持一个静态块
471
472**规则:**`arkts-no-multiple-static-blocks`
473
474**级别:错误**
475
476ArkTS不允许类中有多个静态块,如果存在多个静态块语句,请合并到一个静态块中。
477
478**TypeScript**
479
480```typescript
481class C {
482  static s: string
483
484  static {
485    C.s = 'aa'
486  }
487  static {
488    C.s = C.s + 'bb'
489  }
490}
491```
492
493**ArkTS**
494
495```typescript
496class C {
497  static s: string
498
499  static {
500    C.s = 'aa'
501    C.s = C.s + 'bb'
502  }
503}
504```
505
506**说明**
507
508当前不支持静态块的语法。支持该语法后,在.ets文件中使用静态块须遵循本约束。
509
510### 不支持index signature
511
512**规则:**`arkts-no-indexed-signatures`
513
514**级别:错误**
515
516ArkTS不允许index signature,改用数组。
517
518**TypeScript**
519
520```typescript
521// 带index signature的接口:
522interface StringArray {
523  [index: number]: string
524}
525
526function getStringArray(): StringArray {
527  return ['a', 'b', 'c'];
528}
529
530const myArray: StringArray = getStringArray();
531const secondItem = myArray[1];
532```
533
534**ArkTS**
535
536```typescript
537class X {
538  public f: string[] = []
539}
540
541let myArray: X = new X();
542const secondItem = myArray.f[1];
543```
544
545### 使用继承而非intersection type
546
547**规则:**`arkts-no-intersection-types`
548
549**级别:错误**
550
551目前ArkTS不支持intersection type,可以使用继承作为替代方案。
552
553**TypeScript**
554
555```typescript
556interface Identity {
557  id: number
558  name: string
559}
560
561interface Contact {
562  email: string
563  phoneNumber: string
564}
565
566type Employee = Identity & Contact
567```
568
569**ArkTS**
570
571```typescript
572interface Identity {
573  id: number
574  name: string
575}
576
577interface Contact {
578  email: string
579  phoneNumber: string
580}
581
582interface Employee extends Identity,  Contact {}
583```
584
585### 不支持`this`类型
586
587**规则:**`arkts-no-typing-with-this`
588
589**级别:错误**
590
591ArkTS不支持`this`类型,改用显式具体类型。
592
593**TypeScript**
594
595```typescript
596interface ListItem {
597  getHead(): this
598}
599
600class C {
601  n: number = 0
602
603  m(c: this) {
604    // ...
605  }
606}
607```
608
609**ArkTS**
610
611```typescript
612interface ListItem {
613  getHead(): ListItem
614}
615
616class C {
617  n: number = 0
618
619  m(c: C) {
620    // ...
621  }
622}
623```
624
625### 不支持条件类型
626
627**规则:**`arkts-no-conditional-types`
628
629**级别:错误**
630
631ArkTS不支持条件类型别名,引入带显式约束的新类型,或使用`Object`重写逻辑。
632不支持`infer`关键字。
633
634**TypeScript**
635
636```typescript
637type X<T> = T extends number ? T: never
638type Y<T> = T extends Array<infer Item> ? Item: never
639```
640
641**ArkTS**
642
643```typescript
644// 在类型别名中提供显式约束
645type X1<T extends number> = T
646
647// 用Object重写,类型控制较少,需要更多的类型检查以确保安全
648type X2<T> = Object
649
650// Item必须作为泛型参数使用,并能正确实例化
651type YI<Item, T extends Array<Item>> = Item
652```
653
654### 不支持在`constructor`中声明字段
655
656**规则:**`arkts-no-ctor-prop-decls`
657
658**级别:错误**
659
660ArkTS不支持在`constructor`中声明类字段。在`class`中声明这些字段。
661
662**TypeScript**
663
664```typescript
665class Person {
666  constructor(
667    protected ssn: string,
668    private firstName: string,
669    private lastName: string
670  ) {
671    this.ssn = ssn;
672    this.firstName = firstName;
673    this.lastName = lastName;
674  }
675
676  getFullName(): string {
677    return this.firstName + ' ' + this.lastName;
678  }
679}
680```
681
682**ArkTS**
683
684```typescript
685class Person {
686  protected ssn: string
687  private firstName: string
688  private lastName: string
689
690  constructor(ssn: string, firstName: string, lastName: string) {
691    this.ssn = ssn;
692    this.firstName = firstName;
693    this.lastName = lastName;
694  }
695
696  getFullName(): string {
697    return this.firstName + ' ' + this.lastName;
698  }
699}
700```
701
702### 接口中不支持构造签名
703
704**规则:**`arkts-no-ctor-signatures-iface`
705
706**级别:错误**
707
708ArkTS不支持在接口中使用构造签名。改用函数或者方法。
709
710**TypeScript**
711
712```typescript
713interface I {
714  new (s: string): I
715}
716
717function fn(i: I) {
718  return new i('hello');
719}
720```
721
722**ArkTS**
723
724```typescript
725interface I {
726  create(s: string): I
727}
728
729function fn(i: I) {
730  return i.create('hello');
731}
732```
733
734### 不支持索引访问类型
735
736**规则:**`arkts-no-aliases-by-index`
737
738**级别:错误**
739
740ArkTS不支持索引访问类型。
741
742### 不支持通过索引访问字段
743
744**规则:**`arkts-no-props-by-index`
745
746**级别:错误**
747
748ArkTS不支持动态声明字段,不支持动态访问字段。只能访问已在类中声明或者继承可见的字段,访问其他字段将会造成编译时错误。
749使用点操作符访问字段,例如(`obj.field`),不支持索引访问(`obj[field]`)。
750ArkTS支持通过索引访问`TypedArray`(例如`Int32Array`)中的元素。
751
752**TypeScript**
753
754```typescript
755class Point {
756  x: string = ''
757  y: string = ''
758}
759let p: Point = {x: '1', y: '2'};
760console.log(p['x']);
761
762class Person {
763  name: string = ''
764  age: number = 0;
765  [key: string]: string | number
766}
767
768let person: Person = {
769  name: 'John',
770  age: 30,
771  email: '***@example.com',
772  phoneNumber: '18*********',
773}
774```
775
776**ArkTS**
777
778```typescript
779class Point {
780  x: string = ''
781  y: string = ''
782}
783let p: Point = {x: '1', y: '2'};
784console.log(p.x);
785
786class Person {
787  name: string
788  age: number
789  email: string
790  phoneNumber: string
791
792  constructor(name: string, age: number, email: string,
793        phoneNumber: string) {
794    this.name = name;
795    this.age = age;
796    this.email = email;
797    this.phoneNumber = phoneNumber;
798  }
799}
800
801let person = new Person('John', 30, '***@example.com', '18*********');
802console.log(person['name']);     // 编译时错误
803console.log(person.unknownProperty); // 编译时错误
804
805let arr = new Int32Array(1);
806arr[0];
807```
808
809### 不支持structural typing
810
811**规则:**`arkts-no-structural-typing`
812
813**级别:错误**
814
815ArkTS不支持structural typing,编译器无法比较两种类型的`public`API并决定它们是否相同。使用其他机制,例如继承、接口或类型别名。
816
817**TypeScript**
818
819```typescript
820interface I1 {
821  f(): string
822}
823
824interface I2 { // I2等价于I1
825  f(): string
826}
827
828class X {
829  n: number = 0
830  s: string = ''
831}
832
833class Y { // Y等价于X
834  n: number = 0
835  s: string = ''
836}
837
838let x = new X();
839let y = new Y();
840
841console.log('Assign X to Y');
842y = x;
843
844console.log('Assign Y to X');
845x = y;
846
847function foo(x: X) {
848  console.log(x.n + x.s);
849}
850
851// 由于X和Y的API是等价的,所以X和Y是等价的
852foo(new X());
853foo(new Y());
854```
855
856**ArkTS**
857
858```typescript
859interface I1 {
860  f(): string
861}
862
863type I2 = I1 // I2是I1的别名
864
865class B {
866  n: number = 0
867  s: string = ''
868}
869
870// D是B的继承类,构建了子类型和父类型的关系
871class D extends B {
872  constructor() {
873    super()
874  }
875}
876
877let b = new B();
878let d = new D();
879
880console.log('Assign D to B');
881b = d; // 合法赋值,因为B是D的父类
882
883// 将b赋值给d将会引起编译时错误
884// d = b
885
886interface Z {
887   n: number
888   s: string
889}
890
891// 类X implements 接口Z,构建了X和Y的关系
892class X implements Z {
893  n: number = 0
894  s: string = ''
895}
896
897// 类Y implements 接口Z,构建了X和Y的关系
898class Y implements Z {
899  n: number = 0
900  s: string = ''
901}
902
903let x: Z = new X();
904let y: Z = new Y();
905
906console.log('Assign X to Y');
907y = x // 合法赋值,它们是相同的类型
908
909console.log('Assign Y to X');
910x = y // 合法赋值,它们是相同的类型
911
912function foo(c: Z): void {
913  console.log(c.n + c.s);
914}
915
916// 类X和类Y implement 相同的接口,因此下面的两个函数调用都是合法的
917foo(new X());
918foo(new Y());
919```
920
921### 需要显式标注泛型函数类型实参
922
923**规则:**`arkts-no-inferred-generic-params`
924
925**级别:错误**
926
927如果可以从传递给泛型函数的参数中推断出具体类型,ArkTS允许省略泛型类型实参。否则,省略泛型类型实参会发生编译时错误。
928禁止仅基于泛型函数返回类型推断泛型类型参数。
929
930**TypeScript**
931
932```typescript
933function choose<T>(x: T, y: T): T {
934  return Math.random() < 0.5 ? x: y;
935}
936
937let x = choose(10, 20);   // 推断choose<number>(...)
938let y = choose('10', 20); // 编译时错误
939
940function greet<T>(): T {
941  return 'Hello' as T;
942}
943let z = greet() // T的类型被推断为“unknown”
944```
945
946**ArkTS**
947
948```typescript
949function choose<T>(x: T, y: T): T {
950  return Math.random() < 0.5 ? x: y;
951}
952
953let x = choose(10, 20);   // 推断choose<number>(...)
954let y = choose('10', 20); // 编译时错误
955
956function greet<T>(): T {
957  return 'Hello' as T;
958}
959let z = greet<string>();
960```
961
962### 需要显式标注对象字面量的类型
963
964**规则:**`arkts-no-untyped-obj-literals`
965
966**级别:错误**
967
968在ArkTS中,需要显式标注对象字面量的类型,否则,将发生编译时错误。在某些场景下,编译器可以根据上下文推断出字面量的类型。
969
970在以下上下文中不支持使用字面量初始化类和接口:
971
972* 初始化具有`any`、`Object`或`object`类型的任何对象
973* 初始化带有方法的类或接口
974* 初始化包含自定义含参数的构造函数的类
975* 初始化带`readonly`字段的类
976
977**例子1**
978
979**TypeScript**
980
981```typescript
982let o1 = {n: 42, s: 'foo'};
983let o2: Object = {n: 42, s: 'foo'};
984let o3: object = {n: 42, s: 'foo'};
985
986let oo: Object[] = [{n: 1, s: '1'}, {n: 2, s: '2'}];
987```
988
989**ArkTS**
990
991```typescript
992class C1 {
993  n: number = 0
994  s: string = ''
995}
996
997let o1: C1 = {n: 42, s: 'foo'};
998let o2: C1 = {n: 42, s: 'foo'};
999let o3: C1 = {n: 42, s: 'foo'};
1000
1001let oo: C1[] = [{n: 1, s: '1'}, {n: 2, s: '2'}];
1002```
1003
1004**例子2**
1005
1006**TypeScript**
1007
1008```typescript
1009class C2 {
1010  s: string
1011  constructor(s: string) {
1012    this.s = 's =' + s;
1013  }
1014}
1015let o4: C2 = {s: 'foo'};
1016```
1017
1018**ArkTS**
1019
1020```typescript
1021class C2 {
1022  s: string
1023  constructor(s: string) {
1024    this.s = 's =' + s;
1025  }
1026}
1027let o4 = new C2('foo');
1028```
1029
1030**例子3**
1031
1032**TypeScript**
1033
1034```typescript
1035class C3 {
1036  readonly n: number = 0
1037  readonly s: string = ''
1038}
1039let o5: C3 = {n: 42, s: 'foo'};
1040```
1041
1042**ArkTS**
1043
1044```typescript
1045class C3 {
1046  n: number = 0
1047  s: string = ''
1048}
1049let o5: C3 = {n: 42, s: 'foo'};
1050```
1051
1052**例子4**
1053
1054**TypeScript**
1055
1056```typescript
1057abstract class A {}
1058let o6: A = {};
1059```
1060
1061**ArkTS**
1062
1063```typescript
1064abstract class A {}
1065class C extends A {}
1066let o6: C = {}; // 或 let o6: C = new C()
1067```
1068
1069**例子5**
1070
1071**TypeScript**
1072
1073```typescript
1074class C4 {
1075  n: number = 0
1076  s: string = ''
1077  f() {
1078    console.log('Hello');
1079  }
1080}
1081let o7: C4 = {n: 42, s: 'foo', f: () => {}};
1082```
1083
1084**ArkTS**
1085
1086```typescript
1087class C4 {
1088  n: number = 0
1089  s: string = ''
1090  f() {
1091    console.log('Hello');
1092  }
1093}
1094let o7 = new C4();
1095o7.n = 42;
1096o7.s = 'foo';
1097```
1098
1099**例子6**
1100
1101**TypeScript**
1102
1103```typescript
1104class Point {
1105  x: number = 0
1106  y: number = 0
1107}
1108
1109function getPoint(o: Point): Point {
1110  return o;
1111}
1112
1113// TS支持structural typing,可以推断p的类型为Point
1114let p = {x: 5, y: 10};
1115getPoint(p);
1116
1117// 可通过上下文推断出对象字面量的类型为Point
1118getPoint({x: 5, y: 10});
1119```
1120
1121**ArkTS**
1122
1123```typescript
1124class Point {
1125  x: number = 0
1126  y: number = 0
1127
1128  // 在字面量初始化之前,使用constructor()创建一个有效对象。
1129  // 由于没有为Point定义构造函数,编译器将自动添加一个默认构造函数。
1130}
1131
1132function getPoint(o: Point): Point {
1133  return o;
1134}
1135
1136// 字面量初始化需要显式定义类型
1137let p: Point = {x: 5, y: 10};
1138getPoint(p);
1139
1140// getPoint接受Point类型,字面量初始化生成一个Point的新实例
1141getPoint({x: 5, y: 10});
1142```
1143
1144### 对象字面量不能用于类型声明
1145
1146**规则:**`arkts-no-obj-literals-as-types`
1147
1148**级别:错误**
1149
1150ArkTS不支持使用对象字面量声明类型,可以使用类或者接口声明类型。
1151
1152**TypeScript**
1153
1154```typescript
1155let o: {x: number, y: number} = {
1156  x: 2,
1157  y: 3
1158}
1159
1160type S = Set<{x: number, y: number}>
1161```
1162
1163**ArkTS**
1164
1165```typescript
1166class O {
1167  x: number = 0
1168  y: number = 0
1169}
1170
1171let o: O = {x: 2, y: 3};
1172
1173type S = Set<O>
1174```
1175
1176### 数组字面量必须仅包含可推断类型的元素
1177
1178**规则:**`arkts-no-noninferrable-arr-literals`
1179
1180**级别:错误**
1181
1182本质上,ArkTS将数组字面量的类型推断为数组所有元素的联合类型。如果其中任何一个元素的类型无法根据上下文推导出来(例如,无类型的对象字面量),则会发生编译时错误。
1183
1184**TypeScript**
1185
1186```typescript
1187let a = [{n: 1, s: '1'}, {n: 2, s: '2'}];
1188```
1189
1190**ArkTS**
1191
1192```typescript
1193class C {
1194  n: number = 0
1195  s: string = ''
1196}
1197
1198let a1 = [{n: 1, s: '1'} as C, {n: 2, s: '2'} as C]; // a1的类型为“C[]”
1199let a2: C[] = [{n: 1, s: '1'}, {n: 2, s: '2'}];    // a2的类型为“C[]”
1200```
1201
1202### 使用箭头函数而非函数表达式
1203
1204**规则:**`arkts-no-func-expressions`
1205
1206**级别:错误**
1207
1208ArkTS不支持函数表达式,使用箭头函数。
1209
1210**TypeScript**
1211
1212```typescript
1213let f = function (s: string) {
1214  console.log(s);
1215}
1216```
1217
1218**ArkTS**
1219
1220```typescript
1221let f = (s: string) => {
1222  console.log(s);
1223}
1224```
1225
1226### 不支持使用类表达式
1227
1228**规则:**`arkts-no-class-literals`
1229
1230**级别:错误**
1231
1232ArkTS不支持使用类表达式,必须显式声明一个类。
1233
1234**TypeScript**
1235
1236```typescript
1237const Rectangle = class {
1238  constructor(height: number, width: number) {
1239    this.height = height;
1240    this.width = width;
1241  }
1242
1243  height
1244  width
1245}
1246
1247const rectangle = new Rectangle(0.0, 0.0);
1248```
1249
1250**ArkTS**
1251
1252```typescript
1253class Rectangle {
1254  constructor(height: number, width: number) {
1255    this.height = height;
1256    this.width = width;
1257  }
1258
1259  height: number
1260  width: number
1261}
1262
1263const rectangle = new Rectangle(0.0, 0.0);
1264```
1265
1266### 类不允许`implements`
1267
1268**规则:**`arkts-implements-only-iface`
1269
1270**级别:错误**
1271
1272ArkTS不允许类被`implements`,只有接口可以被`implements`。
1273
1274**TypeScript**
1275
1276```typescript
1277class C {
1278  foo() {}
1279}
1280
1281class C1 implements C {
1282  foo() {}
1283}
1284```
1285
1286**ArkTS**
1287
1288```typescript
1289interface C {
1290  foo(): void
1291}
1292
1293class C1 implements C {
1294  foo() {}
1295}
1296```
1297
1298### 不支持修改对象的方法
1299
1300**规则:**`arkts-no-method-reassignment`
1301
1302**级别:错误**
1303
1304ArkTS不支持修改对象的方法。在静态语言中,对象的布局是确定的。一个类的所有对象实例享有同一个方法。
1305如果需要为某个特定的对象增加方法,可以封装函数或者使用继承的机制。
1306
1307**TypeScript**
1308
1309```typescript
1310class C {
1311  foo() {
1312    console.log('foo');
1313  }
1314}
1315
1316function bar() {
1317  console.log('bar');
1318}
1319
1320let c1 = new C();
1321let c2 = new C();
1322c2.foo = bar;
1323
1324c1.foo(); // foo
1325c2.foo(); // bar
1326```
1327
1328**ArkTS**
1329
1330```typescript
1331class C {
1332  foo() {
1333    console.log('foo');
1334  }
1335}
1336
1337class Derived extends C {
1338  foo() {
1339    console.log('Extra');
1340    super.foo();
1341  }
1342}
1343
1344function bar() {
1345  console.log('bar');
1346}
1347
1348let c1 = new C();
1349let c2 = new C();
1350c1.foo(); // foo
1351c2.foo(); // foo
1352
1353let c3 = new Derived();
1354c3.foo(); // Extra foo
1355```
1356
1357### 类型转换仅支持`as T`语法
1358
1359**规则:**`arkts-as-casts`
1360
1361**级别:错误**
1362
1363在ArkTS中,`as`关键字是类型转换的唯一语法,错误的类型转换会导致编译时错误或者运行时抛出`ClassCastException`异常。ArkTS不支持使用`<type>`语法进行类型转换。
1364
1365当需要将`primitive`类型(如`number`或`boolean`)转换成引用类型时,请使用`new`表达式。
1366
1367**TypeScript**
1368
1369```typescript
1370class Shape {}
1371class Circle extends Shape { x: number = 5 }
1372class Square extends Shape { y: string = 'a' }
1373
1374function createShape(): Shape {
1375  return new Circle();
1376}
1377
1378let c1 = <Circle> createShape();
1379
1380let c2 = createShape() as Circle;
1381
1382// 如果转换错误,不会产生编译时或运行时报错
1383let c3 = createShape() as Square;
1384console.log(c3.y); // undefined
1385
1386// 在TS中,由于`as`关键字不会在运行时生效,所以`instanceof`的左操作数不会在运行时被装箱成引用类型
1387let e1 = (5.0 as Number) instanceof Number; // false
1388
1389// 创建Number对象,获得预期结果:
1390let e2 = (new Number(5.0)) instanceof Number; // true
1391```
1392
1393**ArkTS**
1394
1395```typescript
1396class Shape {}
1397class Circle extends Shape { x: number = 5 }
1398class Square extends Shape { y: string = 'a' }
1399
1400function createShape(): Shape {
1401  return new Circle();
1402}
1403
1404let c2 = createShape() as Circle;
1405
1406// 运行时抛出ClassCastException异常:
1407let c3 = createShape() as Square;
1408
1409// 创建Number对象,获得预期结果:
1410let e2 = (new Number(5.0)) instanceof Number; // true
1411```
1412
1413### 不支持JSX表达式
1414
1415**规则:**`arkts-no-jsx`
1416
1417**级别:错误**
1418
1419不支持使用JSX。
1420
1421### 一元运算符`+`、`-`和`~`仅适用于数值类型
1422
1423**规则:**`arkts-no-polymorphic-unops`
1424
1425**级别:错误**
1426
1427ArkTS仅允许一元运算符用于数值类型,否则会发生编译时错误。与TypeScript不同,ArkTS不支持隐式将字符串转换成数值,必须进行显式转换。
1428
1429**TypeScript**
1430
1431```typescript
1432let a = +5;    // 5(number类型)
1433let b = +'5';    // 5(number类型)
1434let c = -5;    // -5(number类型)
1435let d = -'5';    // -5(number类型)
1436let e = ~5;    // -6(number类型)
1437let f = ~'5';    // -6(number类型)
1438let g = +'string'; // NaN(number类型)
1439
1440function returnTen(): string {
1441  return '-10';
1442}
1443
1444function returnString(): string {
1445  return 'string';
1446}
1447
1448let x = +returnTen();  // -10(number类型)
1449let y = +returnString(); // NaN
1450```
1451
1452**ArkTS**
1453
1454```typescript
1455let a = +5;    // 5(number类型)
1456let b = +'5';    // 编译时错误
1457let c = -5;    // -5(number类型)
1458let d = -'5';    // 编译时错误
1459let e = ~5;    // -6(number类型)
1460let f = ~'5';    // 编译时错误
1461let g = +'string'; // 编译时错误
1462
1463function returnTen(): string {
1464  return '-10';
1465}
1466
1467function returnString(): string {
1468  return 'string';
1469}
1470
1471let x = +returnTen();  // 编译时错误
1472let y = +returnString(); // 编译时错误
1473```
1474
1475### 不支持`delete`运算符
1476
1477**规则:**`arkts-no-delete`
1478
1479**级别:错误**
1480
1481ArkTS中,对象布局在编译时就确定了,且不能在运行时被更改。因此,删除属性的操作没有意义。
1482
1483**TypeScript**
1484
1485```typescript
1486class Point {
1487  x?: number = 0.0
1488  y?: number = 0.0
1489}
1490
1491let p = new Point();
1492delete p.y;
1493```
1494
1495**ArkTS**
1496
1497```typescript
1498// 可以声明一个可空类型并使用null作为缺省值
1499class Point {
1500  x: number | null = 0
1501  y: number | null = 0
1502}
1503
1504let p = new Point();
1505p.y = null;
1506```
1507
1508### 仅允许在表达式中使用`typeof`运算符
1509
1510**规则:**`arkts-no-type-query`
1511
1512**级别:错误**
1513
1514ArkTS仅支持在表达式中使用`typeof`运算符,不允许使用`typeof`作为类型。
1515
1516**TypeScript**
1517
1518```typescript
1519let n1 = 42;
1520let s1 = 'foo';
1521console.log(typeof n1); // 'number'
1522console.log(typeof s1); // 'string'
1523let n2: typeof n1
1524let s2: typeof s1
1525```
1526
1527**ArkTS**
1528
1529```typescript
1530let n1 = 42;
1531let s1 = 'foo';
1532console.log(typeof n1); // 'number'
1533console.log(typeof s1); // 'string'
1534let n2: number
1535let s2: string
1536```
1537
1538### 部分支持`instanceof`运算符
1539
1540**规则:**`arkts-instanceof-ref-types`
1541
1542**级别:错误**
1543
1544在TypeScript中,`instanceof`运算符的左操作数的类型必须为`any`类型、对象类型,或者它是类型参数,否则结果为`false`。在ArkTS中,`instanceof`运算符的左操作数的类型必须为引用类型(例如,对象、数组或者函数),否则会发生编译时错误。此外,在ArkTS中,`instanceof`运算符的左操作数不能是类型,必须是对象的实例。
1545
1546### 不支持`in`运算符
1547
1548**规则:**`arkts-no-in`
1549
1550**级别:错误**
1551
1552由于在ArkTS中,对象布局在编译时是已知的并且在运行时无法修改,因此,不支持`in`运算符。如果仍需检查某些类成员是否存在,使用`instanceof`代替。
1553
1554**TypeScript**
1555
1556```typescript
1557class Person {
1558  name: string = ''
1559}
1560let p = new Person();
1561
1562let b = 'name' in p; // true
1563```
1564
1565**ArkTS**
1566
1567```typescript
1568class Person {
1569  name: string = ''
1570}
1571let p = new Person();
1572
1573let b = p instanceof Person; // true,且属性name一定存在
1574```
1575
1576### 不支持解构赋值
1577
1578**规则:**`arkts-no-destruct-assignment`
1579
1580**级别:错误**
1581
1582ArkTS不支持解构赋值。可使用其他替代方法,例如,使用临时变量。
1583
1584**TypeScript**
1585
1586```typescript
1587let [one, two] = [1, 2]; // 此处需要分号
1588[one, two] = [two, one];
1589
1590let head, tail
1591[head, ...tail] = [1, 2, 3, 4];
1592```
1593
1594**ArkTS**
1595
1596```typescript
1597let arr: number[] = [1, 2];
1598let one = arr[0];
1599let two = arr[1];
1600
1601let tmp = one;
1602one = two;
1603two = tmp;
1604
1605let data: Number[] = [1, 2, 3, 4];
1606let head = data[0];
1607let tail: Number[] = [];
1608for (let i = 1; i < data.length; ++i) {
1609  tail.push(data[i]);
1610}
1611```
1612
1613### 逗号运算符`,`仅用在`for`循环语句中
1614
1615**规则:**`arkts-no-comma-outside-loops`
1616
1617**级别:错误**
1618
1619为了方便理解执行顺序,在ArkTS中,逗号运算符仅适用于`for`循环语句中。注意与声明变量、函数参数传递时的逗号分隔符不同。
1620
1621**TypeScript**
1622
1623```typescript
1624for (let i = 0, j = 0; i < 10; ++i, j += 2) {
1625  // ...
1626}
1627
1628let x = 0;
1629x = (++x, x++); // 1
1630```
1631
1632**ArkTS**
1633
1634```typescript
1635for (let i = 0, j = 0; i < 10; ++i, j += 2) {
1636  // ...
1637}
1638
1639// 通过语句表示执行顺序,而非逗号运算符
1640let x = 0;
1641++x;
1642x = x++;
1643```
1644
1645### 不支持解构变量声明
1646
1647**规则:**`arkts-no-destruct-decls`
1648
1649**级别:错误**
1650
1651ArkTS不支持解构变量声明。它是一个依赖于结构兼容性的动态特性并且解构声明中的名称必须和被解构对象中的属性名称一致。
1652
1653**TypeScript**
1654
1655```typescript
1656class Point {
1657  x: number = 0.0
1658  y: number = 0.0
1659}
1660
1661function returnZeroPoint(): Point {
1662  return new Point();
1663}
1664
1665let {x, y} = returnZeroPoint();
1666```
1667
1668**ArkTS**
1669
1670```typescript
1671class Point {
1672  x: number = 0.0
1673  y: number = 0.0
1674}
1675
1676function returnZeroPoint(): Point {
1677  return new Point();
1678}
1679
1680// 创建一个局部变量来处理每个字段
1681let zp = returnZeroPoint();
1682let x = zp.x;
1683let y = zp.y;
1684```
1685
1686### 不支持在catch语句标注类型
1687
1688**规则:**`arkts-no-types-in-catch`
1689
1690**级别:错误**
1691
1692在TypeScript的catch语句中,只能标注`any`或`unknown`类型。由于ArkTS不支持这些类型,应省略类型标注。
1693
1694**TypeScript**
1695
1696```typescript
1697try {
1698  // ...
1699} catch (a: unknown) {
1700  // 处理异常
1701}
1702```
1703
1704**ArkTS**
1705
1706```typescript
1707try {
1708  // ...
1709} catch (a) {
1710  // 处理异常
1711}
1712```
1713
1714### 不支持`for .. in`
1715
1716**规则:**`arkts-no-for-in`
1717
1718**级别:错误**
1719
1720由于在ArkTS中,对象布局在编译时是确定的、并且不能在运行时被改变,所以不支持使用`for .. in`迭代一个对象的属性。对于数组来说,可以使用常规的`for`循环。
1721
1722**TypeScript**
1723
1724```typescript
1725let a: string[] = ['1.0', '2.0', '3.0'];
1726for (let i in a) {
1727  console.log(a[i]);
1728}
1729```
1730
1731**ArkTS**
1732
1733```typescript
1734let a: string[] = ['1.0', '2.0', '3.0'];
1735for (let i = 0; i < a.length; ++i) {
1736  console.log(a[i]);
1737}
1738```
1739
1740### 不支持映射类型
1741
1742**规则:**`arkts-no-mapped-types`
1743
1744**级别:错误**
1745
1746ArkTS不支持映射类型,使用其他语法来表示相同的语义。
1747
1748**TypeScript**
1749
1750```typescript
1751type OptionsFlags<Type> = {
1752  [Property in keyof Type]: boolean
1753}
1754```
1755
1756**ArkTS**
1757
1758```typescript
1759class C {
1760  n: number = 0
1761  s: string = ''
1762}
1763
1764class CFlags {
1765  n: boolean = false
1766  s: boolean = false
1767}
1768```
1769
1770### 不支持`with`语句
1771
1772**规则:**`arkts-no-with`
1773
1774**级别:错误**
1775
1776ArkTS不支持`with`语句,使用其他语法来表示相同的语义。
1777
1778**TypeScript**
1779
1780```typescript
1781with (Math) { // 编译时错误, 但是仍能生成JavaScript代码
1782  let r: number = 42;
1783  let area: number = PI * r * r;
1784}
1785```
1786
1787**ArkTS**
1788
1789```typescript
1790let r: number = 42;
1791let area: number = Math.PI * r * r;
1792```
1793
1794### 限制`throw`语句中表达式的类型
1795
1796**规则:**`arkts-limited-throw`
1797
1798**级别:错误**
1799
1800ArkTS只支持抛出`Error`类或其派生类的实例。禁止抛出其他类型(例如`number`或`string`)的数据。
1801
1802**TypeScript**
1803
1804```typescript
1805throw 4;
1806throw '';
1807throw new Error();
1808```
1809
1810**ArkTS**
1811
1812```typescript
1813throw new Error();
1814```
1815
1816### 限制省略函数返回类型标注
1817
1818**规则:**`arkts-no-implicit-return-types`
1819
1820**级别:错误**
1821
1822ArkTS在部分场景中支持对函数返回类型进行推断。当`return`语句中的表达式是对某个函数或方法进行调用,且该函数或方法的返回类型没有被显著标注时,会出现编译时错误。在这种情况下,请标注函数返回类型。
1823
1824**TypeScript**
1825
1826```typescript
1827// 只有在开启noImplicitAny选项时会产生编译时错误
1828function f(x: number) {
1829  if (x <= 0) {
1830    return x;
1831  }
1832  return g(x);
1833}
1834
1835// 只有在开启noImplicitAny选项时会产生编译时错误
1836function g(x: number) {
1837  return f(x - 1);
1838}
1839
1840function doOperation(x: number, y: number) {
1841  return x + y;
1842}
1843
1844f(10);
1845doOperation(2, 3);
1846```
1847
1848**ArkTS**
1849
1850```typescript
1851// 需标注返回类型:
1852function f(x: number): number {
1853  if (x <= 0) {
1854    return x;
1855  }
1856  return g(x);
1857}
1858
1859// 可以省略返回类型,返回类型可以从f的类型标注推导得到
1860function g(x: number): number {
1861  return f(x - 1);
1862}
1863
1864// 可以省略返回类型
1865function doOperation(x: number, y: number) {
1866  return x + y;
1867}
1868
1869f(10);
1870doOperation(2, 3);
1871```
1872
1873### 不支持参数解构的函数声明
1874
1875**规则:**`arkts-no-destruct-params`
1876
1877**级别:错误**
1878
1879ArkTS要求实参必须直接传递给函数,且必须指定到形参。
1880
1881**TypeScript**
1882
1883```typescript
1884function drawText({ text = '', location: [x, y] = [0, 0], bold = false }) {
1885  text;
1886  x;
1887  y;
1888  bold;
1889}
1890
1891drawText({ text: 'Hello, world!', location: [100, 50], bold: true });
1892```
1893
1894**ArkTS**
1895
1896```typescript
1897function drawText(text: String, location: number[], bold: boolean) {
1898  let x = location[0];
1899  let y = location[1];
1900  text;
1901  x;
1902  y;
1903  bold;
1904}
1905
1906function main() {
1907  drawText('Hello, world!', [100, 50], true);
1908}
1909```
1910
1911### 不支持在函数内声明函数
1912
1913**规则:**`arkts-no-nested-funcs`
1914
1915**级别:错误**
1916
1917ArkTS不支持在函数内声明函数,改用lambda函数。
1918
1919**TypeScript**
1920
1921```typescript
1922function addNum(a: number, b: number): void {
1923
1924  // 函数内声明函数
1925  function logToConsole(message: string): void {
1926    console.log(message);
1927  }
1928
1929  let result = a + b;
1930
1931  // 调用函数
1932  logToConsole('result is ' + result);
1933}
1934```
1935
1936**ArkTS**
1937
1938```typescript
1939function addNum(a: number, b: number): void {
1940  // 使用lambda函数代替声明函数
1941  let logToConsole: (message: string) => void = (message: string): void => {
1942    console.log(message);
1943  }
1944
1945  let result = a + b;
1946
1947  logToConsole('result is ' + result);
1948}
1949```
1950
1951### 不支持在函数和类的静态方法中使用`this`
1952
1953**规则:**`arkts-no-standalone-this`
1954
1955**级别:错误**
1956
1957ArkTS不支持在函数和类的静态方法中使用`this`,只能在类的实例方法中使用`this`。
1958
1959**TypeScript**
1960
1961```typescript
1962function foo(i: string) {
1963  this.count = i; // 只有在开启noImplicitThis选项时会产生编译时错误
1964}
1965
1966class A {
1967  count: string = 'a'
1968  m = foo
1969}
1970
1971let a = new A();
1972console.log(a.count); // 打印a
1973a.m('b');
1974console.log(a.count); // 打印b
1975```
1976
1977**ArkTS**
1978
1979```typescript
1980class A {
1981  count: string = 'a'
1982  m(i: string): void {
1983    this.count = i;
1984  }
1985}
1986
1987function main(): void {
1988  let a = new A();
1989  console.log(a.count);  // 打印a
1990  a.m('b');
1991  console.log(a.count);  // 打印b
1992}
1993```
1994
1995### 不支持生成器函数
1996
1997**规则:**`arkts-no-generators`
1998
1999**级别:错误**
2000
2001目前ArkTS不支持生成器函数,使用`async`或`await`机制进行并行任务处理。
2002
2003**TypeScript**
2004
2005```typescript
2006function* counter(start: number, end: number) {
2007  for (let i = start; i <= end; i++) {
2008    yield i;
2009  }
2010}
2011
2012for (let num of counter(1, 5)) {
2013  console.log(num);
2014}
2015```
2016
2017**ArkTS**
2018
2019```typescript
2020async function complexNumberProcessing(num: number): Promise<number> {
2021  // ...
2022  return num;
2023}
2024
2025async function foo() {
2026  for (let i = 1; i <= 5; i++) {
2027    await complexNumberProcessing(i);
2028  }
2029}
2030
2031foo()
2032```
2033
2034### 使用`instanceof`和`as`进行类型保护
2035
2036**规则:**`arkts-no-is`
2037
2038**级别:错误**
2039
2040ArkTS不支持`is`运算符,必须用`instanceof`运算符替代。在使用之前,必须使用`as`运算符将对象转换为需要的类型。
2041
2042**TypeScript**
2043
2044```typescript
2045class Foo {
2046  foo: string = ''
2047  common: string = ''
2048}
2049
2050class Bar {
2051  bar: string = ''
2052  common: string = ''
2053}
2054
2055function isFoo(arg: any): arg is Foo {
2056  return arg.foo !== undefined;
2057}
2058
2059function doStuff(arg: Foo | Bar) {
2060  if (isFoo(arg)) {
2061    console.log(arg.foo);  // OK
2062    console.log(arg.bar);  // 编译时错误
2063  } else {
2064    console.log(arg.foo);  // 编译时错误
2065    console.log(arg.bar);  // OK
2066  }
2067}
2068
2069doStuff({ foo: 123, common: '123' });
2070doStuff({ bar: 123, common: '123' });
2071```
2072
2073**ArkTS**
2074
2075```typescript
2076class Foo {
2077  foo: string = ''
2078  common: string = ''
2079}
2080
2081class Bar {
2082  bar: string = ''
2083  common: string = ''
2084}
2085
2086function isFoo(arg: Object): boolean {
2087  return arg instanceof Foo;
2088}
2089
2090function doStuff(arg: Object): void {
2091  if (isFoo(arg)) {
2092    let fooArg = arg as Foo;
2093    console.log(fooArg.foo);   // OK
2094    console.log(arg.bar);    // 编译时错误
2095  } else {
2096    let barArg = arg as Bar;
2097    console.log(arg.foo);    // 编译时错误
2098    console.log(barArg.bar);   // OK
2099  }
2100}
2101
2102function main(): void {
2103  doStuff(new Foo());
2104  doStuff(new Bar());
2105}
2106```
2107
2108### 部分支持展开运算符
2109
2110**规则:**`arkts-no-spread`
2111
2112**级别:错误**
2113
2114ArkTS仅支持使用展开运算符展开数组、`Array`的子类和`TypedArray`(例如`Int32Array`)。仅支持使用在以下场景中:
21151. 传递给剩余参数时
21162. 复制一个数组到数组字面量
2117
2118**TypeScript**
2119
2120```typescript
2121function foo(x: number, y: number, z: number) {
2122  // ...
2123}
2124
2125let args: [number, number, number] = [0, 1, 2];
2126foo(...args);
2127```
2128
2129**ArkTS**
2130
2131```typescript
2132function log_numbers(x: number, y: number, z: number) {
2133  // ...
2134}
2135
2136let numbers: number[] = [1, 2, 3];
2137log_numbers(numbers[0], numbers[1], numbers[2]);
2138```
2139
2140**TypeScript**
2141
2142```typescript
2143let point2d = { x: 1, y: 2 };
2144let point3d = { ...point2d, z: 3 };
2145```
2146
2147**ArkTS**
2148
2149```typescript
2150class Point2D {
2151  x: number = 0; y: number = 0
2152}
2153
2154class Point3D {
2155  x: number = 0; y: number = 0; z: number = 0
2156  constructor(p2d: Point2D, z: number) {
2157    this.x = p2d.x;
2158    this.y = p2d.y;
2159    this.z = z;
2160  }
2161}
2162
2163let p3d = new Point3D({ x: 1, y: 2 } as Point2D, 3);
2164
2165class DerivedFromArray extends Uint16Array {};
2166
2167let arr1 = [1, 2, 3];
2168let arr2 = new Uint16Array([4, 5, 6]);
2169let arr3 = new DerivedFromArray([7, 8, 9]);
2170let arr4 = [...arr1, 10, ...arr2, 11, ...arr3];
2171```
2172
2173### 接口不能继承具有相同方法的两个接口
2174
2175**规则:**`arkts-no-extend-same-prop`
2176
2177**级别:错误**
2178
2179在TypeScript中,如果一个接口继承了具有相同方法的两个接口,则该接口必须使用联合类型来声明该方法的返回值类型。在ArkTS中,由于一个接口中不能包含两个无法区分的方法(例如两个参数列表相同但返回类型不同的方法),因此,接口不能继承具有相同方法的两个接口。
2180
2181**TypeScript**
2182
2183```typescript
2184interface Mover {
2185  getStatus(): { speed: number }
2186}
2187interface Shaker {
2188  getStatus(): { frequency: number }
2189}
2190
2191interface MoverShaker extends Mover, Shaker {
2192  getStatus(): {
2193    speed: number
2194    frequency: number
2195  }
2196}
2197
2198class C implements MoverShaker {
2199  private speed: number = 0
2200  private frequency: number = 0
2201
2202  getStatus() {
2203    return { speed: this.speed, frequency: this.frequency };
2204  }
2205}
2206```
2207
2208**ArkTS**
2209
2210```typescript
2211class MoveStatus {
2212  public speed: number
2213  constructor() {
2214    this.speed = 0;
2215  }
2216}
2217interface Mover {
2218  getMoveStatus(): MoveStatus
2219}
2220
2221class ShakeStatus {
2222  public frequency: number
2223  constructor() {
2224    this.frequency = 0;
2225  }
2226}
2227interface Shaker {
2228  getShakeStatus(): ShakeStatus
2229}
2230
2231class MoveAndShakeStatus {
2232  public speed: number
2233  public frequency: number
2234  constructor() {
2235    this.speed = 0;
2236    this.frequency = 0;
2237  }
2238}
2239
2240class C implements Mover, Shaker {
2241  private move_status: MoveStatus
2242  private shake_status: ShakeStatus
2243
2244  constructor() {
2245    this.move_status = new MoveStatus();
2246    this.shake_status = new ShakeStatus();
2247  }
2248
2249  public getMoveStatus(): MoveStatus {
2250    return this.move_status;
2251  }
2252
2253  public getShakeStatus(): ShakeStatus {
2254    return this.shake_status;
2255  }
2256
2257  public getStatus(): MoveAndShakeStatus {
2258    return {
2259      speed: this.move_status.speed,
2260      frequency: this.shake_status.frequency
2261    };
2262  }
2263}
2264```
2265
2266### 不支持声明合并
2267
2268**规则:**`arkts-no-decl-merging`
2269
2270**级别:错误**
2271
2272ArkTS不支持类、接口的声明合并。
2273
2274**TypeScript**
2275
2276```typescript
2277interface Document {
2278  createElement(tagName: any): Element
2279}
2280
2281interface Document {
2282  createElement(tagName: string): HTMLElement
2283}
2284
2285interface Document {
2286  createElement(tagName: number): HTMLDivElement
2287  createElement(tagName: boolean): HTMLSpanElement
2288  createElement(tagName: string, value: number): HTMLCanvasElement
2289}
2290```
2291
2292**ArkTS**
2293
2294```typescript
2295interface Document {
2296  createElement(tagName: number): HTMLDivElement
2297  createElement(tagName: boolean): HTMLSpanElement
2298  createElement(tagName: string, value: number): HTMLCanvasElement
2299  createElement(tagName: string): HTMLElement
2300  createElement(tagName: Object): Element
2301}
2302```
2303
2304### 接口不能继承类
2305
2306**规则:**`arkts-extends-only-class`
2307
2308**级别:错误**
2309
2310ArkTS不支持接口继承类,接口只能继承接口。
2311
2312**TypeScript**
2313
2314```typescript
2315class Control {
2316  state: number = 0
2317}
2318
2319interface SelectableControl extends Control {
2320  select(): void
2321}
2322```
2323
2324**ArkTS**
2325
2326```typescript
2327interface Control {
2328  state: number
2329}
2330
2331interface SelectableControl extends Control {
2332  select(): void
2333}
2334```
2335
2336### 不支持构造函数类型
2337
2338**规则:**`arkts-no-ctor-signatures-funcs`
2339
2340**级别:错误**
2341
2342ArkTS不支持使用构造函数类型,改用lambda函数。
2343
2344**TypeScript**
2345
2346```typescript
2347class Person {
2348  constructor(
2349    name: string,
2350    age: number
2351  ) {}
2352}
2353type PersonCtor = new (name: string, age: number) => Person
2354
2355function createPerson(Ctor: PersonCtor, name: string, age: number): Person
2356{
2357  return new Ctor(name, age);
2358}
2359
2360const person = createPerson(Person, 'John', 30);
2361```
2362
2363**ArkTS**
2364
2365```typescript
2366class Person {
2367  constructor(
2368    name: string,
2369    age: number
2370  ) {}
2371}
2372type PersonCtor = (n: string, a: number) => Person
2373
2374function createPerson(Ctor: PersonCtor, n: string, a: number): Person {
2375  return Ctor(n, a);
2376}
2377
2378let Impersonizer: PersonCtor = (n: string, a: number): Person => {
2379  return new Person(n, a);
2380}
2381
2382const person = createPerson(Impersonizer, 'John', 30);
2383```
2384
2385### 只能使用类型相同的编译时表达式初始化枚举成员
2386
2387**规则:**`arkts-no-enum-mixed-types`
2388
2389**级别:错误**
2390
2391ArkTS不支持使用在运行期间才能计算的表达式来初始化枚举成员。此外,枚举中所有显式初始化的成员必须具有相同的类型。
2392
2393**TypeScript**
2394
2395```typescript
2396enum E1 {
2397  A = 0xa,
2398  B = 0xb,
2399  C = Math.random(),
2400  D = 0xd,
2401  E // 推断出0xe
2402}
2403
2404enum E2 {
2405  A = 0xa,
2406  B = '0xb',
2407  C = 0xc,
2408  D = '0xd'
2409}
2410```
2411
2412**ArkTS**
2413
2414```typescript
2415enum E1 {
2416  A = 0xa,
2417  B = 0xb,
2418  C = 0xc,
2419  D = 0xd,
2420  E // 推断出0xe
2421}
2422
2423enum E2 {
2424  A = '0xa',
2425  B = '0xb',
2426  C = '0xc',
2427  D = '0xd'
2428}
2429```
2430
2431### 不支持`enum`声明合并
2432
2433**规则:**`arkts-no-enum-merging`
2434
2435**级别:错误**
2436
2437ArkTS不支持`enum`声明合并。
2438
2439**TypeScript**
2440
2441```typescript
2442enum ColorSet {
2443  RED,
2444  GREEN
2445}
2446enum ColorSet {
2447  YELLOW = 2
2448}
2449enum ColorSet {
2450  BLACK = 3,
2451  BLUE
2452}
2453```
2454
2455**ArkTS**
2456
2457```typescript
2458enum ColorSet {
2459  RED,
2460  GREEN,
2461  YELLOW,
2462  BLACK,
2463  BLUE
2464}
2465```
2466
2467### 命名空间不能被用作对象
2468
2469**规则:**`arkts-no-ns-as-obj`
2470
2471**级别:错误**
2472
2473ArkTS不支持将命名空间用作对象,可以使用类或模块。
2474
2475**TypeScript**
2476
2477```typescript
2478namespace MyNamespace {
2479  export let x: number
2480}
2481
2482let m = MyNamespace;
2483m.x = 2;
2484```
2485
2486**ArkTS**
2487
2488```typescript
2489namespace MyNamespace {
2490  export let x: number
2491}
2492
2493MyNamespace.x = 2;
2494```
2495
2496### 不支持命名空间中的非声明语句
2497
2498**规则:**`arkts-no-ns-statements`
2499
2500**级别:错误**
2501
2502在ArkTS中,命名空间用于定义标志符可见范围,只在编译时有效。因此,不支持命名空间中的非声明语句。可以将非声明语句写在函数中。
2503
2504**TypeScript**
2505
2506```typescript
2507namespace A {
2508  export let x: number
2509  x = 1;
2510}
2511```
2512
2513**ArkTS**
2514
2515```typescript
2516namespace A {
2517  export let x: number
2518
2519  export function init() {
2520    x = 1;
2521  }
2522}
2523
2524// 调用初始化函数来执行
2525A.init();
2526```
2527
2528### 不支持`require`和`import`赋值表达式
2529
2530**规则:**`arkts-no-require`
2531
2532**级别:错误**
2533
2534ArkTS不支持通过`require`导入,也不支持`import`赋值表达式,改用`import`。
2535
2536**TypeScript**
2537
2538```typescript
2539import m = require('mod')
2540```
2541
2542**ArkTS**
2543
2544```typescript
2545import * as m from 'mod'
2546```
2547
2548### 不支持`export = ...`语法
2549
2550**规则:**`arkts-no-export-assignment`
2551
2552**级别:错误**
2553
2554ArkTS不支持`export = ...`语法,改用常规的`export`或`import`。
2555
2556**TypeScript**
2557
2558```typescript
2559// module1
2560export = Point
2561
2562class Point {
2563  constructor(x: number, y: number) {}
2564  static origin = new Point(0, 0)
2565}
2566
2567// module2
2568import Pt = require('module1')
2569
2570let p = Pt.Point.origin;
2571```
2572
2573**ArkTS**
2574
2575```typescript
2576// module1
2577export class Point {
2578  constructor(x: number, y: number) {}
2579  static origin = new Point(0, 0)
2580}
2581
2582// module2
2583import * as Pt from 'module1'
2584
2585let p = Pt.Point.origin
2586```
2587
2588### 不支持ambient module声明
2589
2590**规则:**`arkts-no-ambient-decls`
2591
2592**级别:错误**
2593
2594由于ArkTS本身有与JavaScript交互的机制,ArkTS不支持ambient module声明。
2595
2596**TypeScript**
2597
2598```typescript
2599declare module 'someModule' {
2600  export function normalize(s: string): string;
2601}
2602```
2603
2604**ArkTS**
2605
2606```typescript
2607// 从原始模块中导入需要的内容
2608import { normalize } from 'someModule'
2609```
2610
2611### 不支持在模块名中使用通配符
2612
2613**规则:**`arkts-no-module-wildcards`
2614
2615**级别:错误**
2616
2617由于在ArkTS中,导入是编译时而非运行时行为,因此,不支持在模块名中使用通配符。
2618
2619**TypeScript**
2620
2621```typescript
2622// 声明
2623declare module '*!text' {
2624  const content: string
2625  export default content
2626}
2627
2628// 使用代码
2629import fileContent from 'some.txt!text'
2630```
2631
2632**ArkTS**
2633
2634```typescript
2635// 声明
2636declare namespace N {
2637  function foo(x: number): number
2638}
2639
2640// 使用代码
2641import * as m from 'module'
2642console.log('N.foo called: ' + N.foo(42));
2643```
2644
2645### 不支持通用模块定义(UMD)
2646
2647**规则:**`arkts-no-umd`
2648
2649**级别:错误**
2650
2651ArkTS不支持通用模块定义(UMD)。因为在ArkTS中没有“脚本”的概念(相对于“模块”)。此外,在ArkTS中,导入是编译时而非运行时特性。改用`export`和`import`语法。
2652
2653**TypeScript**
2654
2655```typescript
2656// math-lib.d.ts
2657export const isPrime(x: number): boolean
2658export as namespace mathLib
2659
2660// 脚本中
2661mathLib.isPrime(2)
2662```
2663
2664**ArkTS**
2665
2666```typescript
2667// math-lib.d.ts
2668namespace mathLib {
2669  export isPrime(x: number): boolean
2670}
2671
2672// 程序中
2673import { mathLib } from 'math-lib'
2674mathLib.isPrime(2)
2675```
2676
2677### 不支持`new.target`
2678
2679**规则:**`arkts-no-new-target`
2680
2681**级别:错误**
2682
2683ArkTS没有原型的概念,因此不支持`new.target`。此特性不符合静态类型的原则。
2684
2685### 不支持确定赋值断言
2686
2687**规则:**`arkts-no-definite-assignment`
2688
2689**级别:警告**
2690
2691ArkTS不支持确定赋值断言,例如:`let v!: T`。改为在声明变量的同时为变量赋值。
2692
2693**TypeScript**
2694
2695```typescript
2696let x!: number // 提示:在使用前将x初始化
2697
2698initialize();
2699
2700function initialize() {
2701  x = 10;
2702}
2703
2704console.log('x = ' + x);
2705```
2706
2707**ArkTS**
2708
2709```typescript
2710function initialize(): number {
2711  return 10;
2712}
2713
2714let x: number = initialize();
2715
2716console.log('x = ' + x);
2717```
2718
2719### 不支持在原型上赋值
2720
2721**规则:**`arkts-no-prototype-assignment`
2722
2723**级别:错误**
2724
2725ArkTS没有原型的概念,因此不支持在原型上赋值。此特性不符合静态类型的原则。
2726
2727**TypeScript**
2728
2729```typescript
2730let C = function(p) {
2731  this.p = p; // 只有在开启noImplicitThis选项时会产生编译时错误
2732}
2733
2734C.prototype = {
2735  m() {
2736    console.log(this.p);
2737  }
2738}
2739
2740C.prototype.q = function(r: string) {
2741  return this.p == r;
2742}
2743```
2744
2745**ArkTS**
2746
2747```typescript
2748class C {
2749  p: string = ''
2750  m() {
2751    console.log(this.p);
2752  }
2753  q(r: string) {
2754    return this.p == r;
2755  }
2756}
2757```
2758
2759### 不支持`globalThis`
2760
2761**规则:**`arkts-no-globalthis`
2762
2763**级别:警告**
2764
2765由于ArkTS不支持动态更改对象的布局,因此不支持全局作用域和`globalThis`。
2766
2767**TypeScript**
2768
2769```typescript
2770// 全局文件中
2771var abc = 100;
2772
2773// 从上面引用'abc'
2774let x = globalThis.abc;
2775```
2776
2777**ArkTS**
2778
2779```typescript
2780// file1
2781export let abc: number = 100;
2782
2783// file2
2784import * as M from 'file1'
2785
2786let x = M.abc;
2787```
2788
2789### 不支持一些utility类型
2790
2791**规则:**`arkts-no-utility-types`
2792
2793**级别:错误**
2794
2795ArkTS仅支持`Partial`、`Required`、`Readonly`和`Record`,不支持TypeScript中其他的`Utility Types`。
2796
2797对于`Record`类型的对象,通过索引访问到的值的类型是包含`undefined`的联合类型。
2798
2799### 不支持对函数声明属性
2800
2801**规则:**`arkts-no-func-props`
2802
2803**级别:错误**
2804
2805由于ArkTS不支持动态改变函数对象布局,因此,不支持对函数声明属性。
2806
2807### 不支持`Function.apply`和`Function.call`
2808
2809**规则:**`arkts-no-func-apply-call`
2810
2811**级别:错误**
2812
2813ArkTS不允许使用标准库函数`Function.apply`和`Function.call`。标准库使用这些函数来显式设置被调用函数的`this`参数。在ArkTS中,`this`的语义仅限于传统的OOP风格,函数体中禁止使用`this`。
2814
2815### 不支持`Function.bind`
2816
2817**规则:**`arkts-no-func-bind`
2818
2819**级别:警告**
2820
2821ArkTS不允许使用标准库函数`Function.bind`。标准库使用这些函数来显式设置被调用函数的`this`参数。在ArkTS中,`this`的语义仅限于传统的OOP风格,函数体中禁止使用`this`。
2822
2823
2824### 不支持`as const`断言
2825
2826**规则:**`arkts-no-as-const`
2827
2828**级别:错误**
2829
2830ArkTS不支持`as const`断言。在标准TypeScript中,`as const`用于标注字面量的相应字面量类型,而ArkTS不支持字面量类型。
2831
2832**TypeScript**
2833
2834```typescript
2835// 'hello'类型
2836let x = 'hello' as const;
2837
2838// 'readonly [10, 20]'类型
2839let y = [10, 20] as const;
2840
2841// '{ readonly text: 'hello' }'类型
2842let z = { text: 'hello' } as const;
2843```
2844
2845**ArkTS**
2846
2847```typescript
2848// 'string'类型
2849let x: string = 'hello';
2850
2851// 'number[]'类型
2852let y: number[] = [10, 20];
2853
2854class Label {
2855  text: string = ''
2856}
2857
2858// 'Label'类型
2859let z: Label = {
2860  text: 'hello'
2861}
2862```
2863
2864### 不支持导入断言
2865
2866**规则:**`arkts-no-import-assertions`
2867
2868**级别:错误**
2869
2870由于在ArkTS中,导入是编译时而非运行时特性,因此,ArkTS不支持导入断言。在运行时检查导入的API是否正确,对于静态类型的语言来说是没有意义的。改用常规的`import`语法。
2871
2872**TypeScript**
2873
2874```typescript
2875import { obj } from 'something.json' assert { type: 'json' }
2876```
2877
2878**ArkTS**
2879
2880```typescript
2881// 编译时将检查导入T的正确性
2882import { something } from 'module'
2883```
2884
2885### 限制使用标准库
2886
2887**规则:**`arkts-limited-stdlib`
2888
2889**级别:错误**
2890
2891ArkTS不允许使用TypeScript或JavaScript标准库中的某些接口。大部分接口与动态特性有关。ArkTS中禁止使用以下接口:
2892
2893全局对象的属性和方法:`eval`
2894
2895`Object`:`__proto__`、`__defineGetter__`、`__defineSetter__`、
2896`__lookupGetter__`、`__lookupSetter__`、`assign`、`create`、
2897`defineProperties`、`defineProperty`、`freeze`、
2898`fromEntries`、`getOwnPropertyDescriptor`、`getOwnPropertyDescriptors`、
2899`getOwnPropertySymbols`、`getPrototypeOf`、
2900`hasOwnProperty`、`is`、`isExtensible`、`isFrozen`、
2901`isPrototypeOf`、`isSealed`、`preventExtensions`、
2902`propertyIsEnumerable`、`seal`、`setPrototypeOf`
2903
2904`Reflect`:`apply`、`construct`、`defineProperty`、`deleteProperty`、
2905`getOwnPropertyDescriptor`、`getPrototypeOf`、
2906`isExtensible`、`preventExtensions`、
2907`setPrototypeOf`
2908
2909`Proxy`:`handler.apply()`、`handler.construct()`、
2910`handler.defineProperty()`、`handler.deleteProperty()`、`handler.get()`、
2911`handler.getOwnPropertyDescriptor()`、`handler.getPrototypeOf()`、
2912`handler.has()`、`handler.isExtensible()`、`handler.ownKeys()`、
2913`handler.preventExtensions()`、`handler.set()`、`handler.setPrototypeOf()`
2914
2915### 强制进行严格类型检查
2916
2917**规则:**`arkts-strict-typing`
2918
2919**级别:错误**
2920
2921在编译阶段,会进行TypeScript严格模式的类型检查,包括:
2922`noImplicitReturns`,
2923`strictFunctionTypes`,
2924`strictNullChecks`,
2925`strictPropertyInitialization`。
2926
2927**TypeScript**
2928
2929```typescript
2930// 只有在开启noImplicitReturns选项时会产生编译时错误
2931function foo(s: string): string {
2932  if (s != '') {
2933    console.log(s);
2934    return s;
2935  } else {
2936    console.log(s);
2937  }
2938}
2939
2940let n: number = null; // 只有在开启strictNullChecks选项时会产生编译时错误
2941```
2942
2943**ArkTS**
2944
2945```typescript
2946function foo(s: string): string {
2947  console.log(s);
2948  return s;
2949}
2950
2951let n1: number | null = null;
2952let n2: number = 0;
2953```
2954
2955在定义类时,如果无法在声明时或者构造函数中初始化某实例属性,那么可以使用确定赋值断言符`!`来消除`strictPropertyInitialization`的报错。
2956
2957使用确定赋值断言符会增加代码错误的风险,开发者需要保证该实例属性在被使用前已被赋值,否则可能会产生运行时异常。
2958
2959使用确定赋值断言符会增加运行时的类型检查,从而增加额外的运行时开销,所以应尽可能避免使用确定赋值断言符。
2960
2961使用确定赋值断言符将产生`warning: arkts-no-definite-assignment`。
2962
2963**TypeScript**
2964
2965```typescript
2966class C {
2967  name: string  // 只有在开启strictPropertyInitialization选项时会产生编译时错误
2968  age: number   // 只有在开启strictPropertyInitialization选项时会产生编译时错误
2969}
2970
2971let c = new C();
2972```
2973
2974**ArkTS**
2975
2976```typescript
2977class C {
2978  name: string = ''
2979  age!: number      // warning: arkts-no-definite-assignment
2980
2981  initAge(age: number) {
2982    this.age = age;
2983  }
2984}
2985
2986let c = new C();
2987c.initAge(10);
2988```
2989
2990### 不允许通过注释关闭类型检查
2991
2992**规则:**`arkts-strict-typing-required`
2993
2994**级别:错误**
2995
2996在ArkTS中,类型检查不是可选项。不允许通过注释关闭类型检查,不支持使用`@ts-ignore`和`@ts-nocheck`。
2997
2998**TypeScript**
2999
3000```typescript
3001// @ts-nocheck
3002// ...
3003// 关闭了类型检查后的代码
3004// ...
3005
3006let s1: string = null; // 没有报错
3007
3008// @ts-ignore
3009let s2: string = null; // 没有报错
3010```
3011
3012**ArkTS**
3013
3014```typescript
3015let s1: string | null = null; // 没有报错,合适的类型
3016let s2: string = null; // 编译时报错
3017```
3018
3019### 允许.ets文件`import`.ets/.ts/.js文件源码, 不允许.ts/.js文件`import`.ets文件源码
3020
3021**规则:**`arkts-no-ts-deps`
3022
3023**级别:错误**
3024
3025.ets文件可以`import`.ets/.ts/.js文件源码,但是.ts/.js文件不允许`import`.ets文件源码。
3026
3027**TypeScript**
3028
3029```typescript
3030// app.ets
3031export class C {
3032  // ...
3033}
3034
3035// lib.ts
3036import { C } from 'app'
3037```
3038
3039**ArkTS**
3040
3041```typescript
3042// lib1.ets
3043export class C {
3044  // ...
3045}
3046
3047// lib2.ets
3048import { C } from 'lib1'
3049```
3050
3051### `class`不能被用作对象
3052
3053**规则:**`arkts-no-classes-as-obj`
3054
3055**级别:警告**
3056
3057在ArkTS中,`class`声明的是一个新的类型,不是一个值。因此,不支持将`class`用作对象(例如将`class`赋值给一个对象)。
3058
3059### 不支持在`import`语句前使用其他语句
3060
3061**规则:**`arkts-no-misplaced-imports`
3062
3063**级别:错误**
3064
3065在ArkTS中,除动态`import`语句外,所有`import`语句需要放在所有其他语句之前。
3066
3067**TypeScript**
3068
3069```typescript
3070class C {
3071  s: string = ''
3072  n: number = 0
3073}
3074
3075import foo from 'module1'
3076```
3077
3078**ArkTS**
3079
3080```typescript
3081import foo from 'module1'
3082
3083class C {
3084  s: string = ''
3085  n: number = 0
3086}
3087
3088import('module2').then(() => {}).catch(() => {})  // 动态import
3089```
3090
3091### 限制使用`ESObject`类型
3092
3093**规则:**`arkts-limited-esobj`
3094
3095**级别:警告**
3096
3097为了防止动态对象(来自.ts/.js文件)在静态代码(.ets文件)中的滥用,`ESObject`类型在ArkTS中的使用是受限的。唯一允许使用`ESObject`类型的场景是将其用在局部变量的声明中。`ESObject`类型变量的赋值也是受限的,只能被来自跨语言调用的对象赋值,例如:`ESObject`、`any`、`unknown`、匿名类型等类型的变量。禁止使用静态类型的值(在.ets文件中定义的)初始化`ESObject`类型变量。`ESObject`类型变量只能用在跨语言调用的函数里或者赋值给另一个`ESObject`类型变量。
3098
3099**ArkTS**
3100
3101```typescript
3102// lib.d.ts
3103declare function foo(): any;
3104declare function bar(a: any): number;
3105
3106// main.ets
3107let e0: ESObject = foo(); // 编译时错误:ESObject类型只能用于局部变量
3108
3109function f() {
3110  let e1 = foo();        // 编译时错误:e1的类型是any
3111  let e2: ESObject = 1;  // 编译时错误:不能用非动态值初始化ESObject类型变量
3112  let e3: ESObject = {}; // 编译时错误:不能用非动态值初始化ESObject类型变量
3113  let e4: ESObject = []; // 编译时错误:不能用非动态值初始化ESObject类型变量
3114  let e5: ESObject = ''; // 编译时错误:不能用非动态值初始化ESObject类型变量
3115  e5['prop'];            // 编译时错误:不能访问ESObject类型变量的属性
3116  e5[1];                 // 编译时错误:不能访问ESObject类型变量的属性
3117  e5.prop;               // 编译时错误:不能访问ESObject类型变量的属性
3118
3119  let e6: ESObject = foo(); // OK,显式标注ESObject类型
3120  let e7 = e6;              // OK,使用ESObject类型赋值
3121  bar(e7);                  // OK,ESObject类型变量传给跨语言调用的函数
3122}
3123```
3124