从TypeScript到ArkTS迁移的保姆级指导

本文通过提供简洁的约束指导如何将标准的TypeScript代码重构为ArkTS代码。尽管ArkTS是基于TypeScript设计的,但出于性能考虑,一些TypeScript的特性被限制了。因此,在ArkTS中,所有的TypeScript特性被分成三类。

  1. 完全支持的特性:原始代码无需任何修改。根据测试,对于已遵循最佳TypeScript实践的项目,代码库中90%到97%的内容可以保持原封不动。
  2. 部分支持的特性:需小规模的代码重构。例如,必须使用关键字let代替var来声明变量。
  3. 不支持的特性:需大规模的代码重构。例如,不支持any类型,所有使用any的代码都需要引入显式类型。

本文将逐一介绍所有部分支持和所有不支持的特性,并提供代码重构的建议。根据本文提供的约束进行代码重构后代码仍为有效的TypeScript代码。对于没有提到的特性,则说明ArkTS完全支持。

示例

包含关键字var的原始TypeScript代码:

function addTen(x: number): number {
    var ten = 10
    return x + ten
}

重构后的代码:

// 重要!重构后仍然是有效的TypeScript代码。
function addTen(x: number): number {
    let ten = 10
    return x + ten
}

不支持的特性

目前,不支持的特性主要包括:

  • 与降低运行时性能的动态类型相关的特性。
  • 需要编译器额外支持从而导致项目构建时间增加的特性。

根据开发者的反馈以及更多实际场景的数据,我们将来可能进一步缩小不支持特性的范围。

概述

本节罗列了ArkTS不支持或部分支持的TypeScript特性。完整的列表以及详细的代码示例和重构建议,请参考约束说明

强制使用静态类型

ArkTS在设计之初,就确定了如下目标:

  • ArkTS代码需非常容易阅读和理解,因为代码的阅读频率高于编写频率。
  • 以最小功耗快速执行代码,这点对于移动设备(ArkTS的目标设备)来说至关重要。

静态类型是ArkTS最重要的特性之一。使用静态类型有助于实现上述两个目标。如果程序采用静态类型,即所有类型在编译时都是已知的,那么开发者就能够容易理解代码中使用了哪些数据结构。同时,由于所有类型在程序实际运行前都是已知的,编译器可以提前验证代码的正确性,从而可以减少运行时的类型检查,有助于性能提升。

基于上述考虑,ArkTS中禁止使用any类型。

示例

//
// 不支持:
//

let res : any = some_api_function("hello", "world")

// `res`是什么?错误代码的数字?字符串?对象?
// 该如何处理它?

//
// 支持:
//

class CallResult {
    public succeeded() : boolean { ... }
    public errorMessage() : string { ... }
}

let res : CallResult = some_api_function("hello", "world")
if (!res.succeeded()) {
    console.log("Call failed: " + res.errorMessage())
}

any类型在TypeScript中并不常见,只有大约1%的TypeScript代码库使用。一些代码检查工具(例如ESLint)也制定一系列规则来禁止使用any。因此,虽然禁止any将导致代码重构,但重构量很小,有助于整体性能提升,因此这个约束是非常有价值的。

禁止在运行时变更对象布局

为实现最佳性能,ArkTS要求在程序执行期间不能更改对象的布局。换句话说,ArkTS禁止以下行为:

  • 向对象中添加新的属性或方法
  • 从对象中删除已有的属性或方法
  • 将任意类型的值赋值给对象属性

TypeScript编译器已经禁止了许多此类操作。然而,有些操作还是有可能绕过编译器的,例如,使用ArkTS不支持的as any进行转换。(详见下面的示例。)

示例

class Point {
    public x : number = 0
    public y : number = 0

    constructor(x : number, y : number) {
        this.x = x
        this.y = y
    }
}

// 无法从对象中删除某个属性,从而确保所有Point对象都具有属性x:
let p1 = new Point(1.0, 1.0)
delete p1.x           // 在TypeScript和ArkTS中,都会产生编译时错误
delete (p1 as any).x  // 在TypeScript中不会报错;在ArkTS中会产生编译时错误

// Point类没有定义命名为z的属性,在程序运行时也无法添加该属性:
let p2 = new Point(2.0, 2.0)
p2.z = "Label";         // 在TypeScript和ArkTS中,都会产生编译时错误
(p2 as any).z = "Label" // 在TypeScript中不会报错;在ArkTS中会产生编译时错误

// 类的定义确保了所有Point对象只有属性x和y,并且无法被添加其他属性:
let p3 = new Point(3.0, 3.0)
let prop = Symbol();     // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
(p3 as any)[prop] = p3.x // 在TypeScript中不会报错;在ArkTS中会产生编译时错误
p3[prop] = p3.x          // 在TypeScript和ArkTS中,都会产生编译时错误

// 类的定义确保了所有Point对象的属性x和y都具有number类型,因此,无法将其他类型的值赋值给它们:
let p4 = new Point(4.0, 4.0)
p4.x = "Hello!";         // 在TypeScript和ArkTS中,都会产生编译时错误
(p4 as any).x = "Hello!" // 在TypeScript中不会报错;在ArkTS中会产生编译时错误

// 使用符合类定义的Point对象:
function distance(p1 : Point, p2 : Point) : number {
    return Math.sqrt(
      (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
    )
}
let p5 = new Point(5.0, 5.0)
let p6 = new Point(6.0, 6.0)
console.log("Distance between p5 and p6: " + distance(p5, p6))

修改对象布局会影响代码的可读性以及运行时性能。从开发者的角度来说,在某处定义类,然后又在其他地方修改实际的对象布局,很容易引起困惑乃至引入错误。此外,这点还需要额外的运行时支持,增加了执行开销。这一点与静态类型的约束也冲突:既然已决定使用显式类型,为什么还需要添加或删除属性呢?

当前,只有少数项目允许在运行时变更对象布局,一些常用的代码检查工具也增加了相应的限制规则。这个约束只会导致少量代码重构,但对性能提升会有积极的影响。

限制运算符的语义

为获得更好的性能并鼓励开发者编写更清晰的代码,ArkTS限制了一些运算符的语义。详细的语义限制,请参考约束说明

示例

// 一元运算符`+`只能作用于数值类型:
console.log(+42)   // 合法运算
console.log(+"42") // 编译时错误

使用额外的语义重载语言运算符会增加语言规范的复杂度,而且,开发者还被迫牢记所有可能的例外情况及对应的处理规则。在某些情况下,产生一些不必要的运行时开销。

当前只有不到1%的代码库使用该特性。因此,尽管限制运算符的语义需要重构代码,但重构量很小且非常容易操作,并且,通过重构能使代码更清晰、具备更高性能。

不支持 structural typing

假设两个不相关的类TU拥有相同的publicAPI:

class T {
    public name : string = ""

    public greet() : void {
        console.log("Hello, " + this.name)
    }
}

class U {
    public name : string = ""

    public greet() : void {
        console.log("Greetings, " + this.name)
    }
}

我们能把类型为T的值赋给类型为U的变量吗?

let u : U = new T() // 是否允许?

我们能把类型为T的值传递给接受类型为U的参数的函数吗?

function greeter(u : U) {
    console.log("To " + u.name)
    u.greet()
}

let t : T = new T()
greeter(t) // 是否允许?

换句话说,我们将采取下面哪种方法呢:

  • TU没有继承关系或没有implements相同的接口,但由于它们具有相同的publicAPI,它们“在某种程度上是相等的”,所以上述两个问题的答案都是“是”;
  • TU没有继承关系或没有implements相同的接口,应当始终被视为完全不同的类型,因此上述两个问题的答案都是“否”。

采用第一种方法的语言支持structural typing,而采用第二种方法的语言则不支持structural typing。目前TypeScript支持structural typing,而ArkTS不支持。

structural typing是否有助于生成清晰、易理解的代码,关于这一点并没有定论。但至少有一点很明确,structural typing不会降低程序的性能(至少在某些时候)。那为什么ArkTS不支持structural typing呢?

因为对structural typing的支持是一个重大的特性,需要在语言规范、编译器和运行时进行大量的考虑和仔细的实现。另外,安全高效的实现还要考虑到其他方面(静态类型、更改对象布局的限制)。鉴于此,当前我们还不支持该特性。根据实际场景的需求和反馈,我们后续会重新加以考虑。更多案例和建议请参考约束说明

约束说明

对象的属性名必须是合法的标识符

规则:arkts-identifiers-as-prop-names

级别:错误

在ArkTS中,对象的属性名不能为数字或字符串。通过属性名访问类的属性,通过数值索引访问数组元素。

TypeScript

var x = {"name": 1, 2: 3}

console.log(x["name"])
console.log(x[2])

ArkTS

class X {
    public name: number = 0
}
let x: X = {name: 1}
console.log(x.name)

let y = [1, 2, 3]
console.log(y[2])

// 在需要通过非标识符(即不同类型的key)获取数据的场景中,使用Map:
let z = new Map()
z.set("name", 1)
z.set(2, 2)
console.log(z.get("name"))
console.log(z.get(2))

相关约束

  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 不支持delete运算符
  • 仅允许在表达式中使用typeof运算符
  • 不支持in运算符
  • 禁止运行时检查对象属性
  • 限制使用标准库
不支持Symbol()API

规则:arkts-no-symbol

级别:错误

TypeScript中的Symbol()API用于在运行时生成唯一的属性名称。由于该API的常见使用场景在静态类型语言中没有意义,因此,ArkTS不支持Symbol()API。在ArkTS中,对象布局在编译时就确定了,且不能在运行时被更改。

ArkTS也不支持Symbol.iteratorIterable interface。请使用数组或容器。

TypeScript

const sym = Symbol()
let o = {
   [sym]: "value"
}

let obj = {
    data: ['a', 'b', 'c'],
    [Symbol.iterator]() {
        const this_ = this
        let index = 0
        return {
            next() {
                return {
                    done: index >= this_.data.length,
                    value: 'name_' + this_.data[index++]
                }
            }
        }
    }
}

for (let t of obj) {
    console.log(t)
}

ArkTS

class SomeClass {
    public someProperty : string = ""
}
let o = new SomeClass()

let arr:string[] = ['a', 'b', 'c']
for (let t of arr) {
    console.log('name_' + t)
}

相关约束

  • 仅支持属性名为标识符的对象
  • 不支持通过索引访问字段
  • 不支持delete运算符
  • 仅允许在表达式中使用typeof运算符
  • 不支持in运算符
  • 禁止运行时检查对象属性
  • 限制使用标准库
不支持以#开头的私有字段

规则:arkts-no-private-identifiers

级别:错误

ArkTS不支持使用#符号开头声明的私有字段。改用private关键字。

TypeScript

class C {
    #foo: number = 42
}

ArkTS

class C {
    private foo: number = 42
}
类型、命名空间的命名必须唯一

规则:arkts-unique-names

级别:错误

类型(类、接口、枚举)、命名空间的命名必须唯一,且与其他名称(例如:变量名、函数名)不同。

TypeScript

let X: string
type X = number[] // 类型的别名与变量同名

ArkTS

let X: string
type T = number[] // 为避免名称冲突,此处不允许使用X
使用let而非var

规则:arkts-no-var

级别:错误

ArkTS不支持 var,请始终使用let代替。

TypeScript

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
       var x = 10
    }
    return x
}

console.log(f(true))  // 10
console.log(f(false)) // undefined

let upper_let = 0
{
    var scoped_var = 0
    let scoped_let = 0
    upper_let = 5
}
scoped_var = 5 // 可见
scoped_let = 5 // 编译时错误

ArkTS

function f(shouldInitialize: boolean): Object {
    let x: Object = new Object()
    if (shouldInitialize) {
        x = 10
    }
    return x
}

console.log(f(true))  // 10
console.log(f(false)) // {}

let upper_let = 0
let scoped_var = 0
{
    let scoped_let = 0
    upper_let = 5
}
scoped_var = 5
scoped_let = 5 //编译时错误
使用具体的类型而非anyunknown

规则:arkts-no-any-unknown

级别:错误

ArkTS不支持anyunknown类型。显式指定具体类型。

TypeScript

let value1 : any
value1 = true
value1 = 42

let value2 : unknown
value2 = true
value2 = 42

ArkTS

let value_b: boolean = true // 或者 let value_b = true
let value_n: number = 42 // 或者 let value_n = 42
let value_o1: Object = true
let value_o2: Object = 42

相关约束

  • 使用Object[]而非tuple
使用Object[]而非tuple

规则:arkts-no-tuples

级别:错误

当前ArkTS不支持tuple。可以使用Object[]来代替tuple。

TypeScript

var t: [number, string] = [3, "three"]
var n = t[0]
var s = t[1]

ArkTS

let t: Object[] = [3, "three"]
let n = t[0]
let s = t[1]
使用class而非具有call signature的类型

规则:arkts-no-call-signatures

级别:错误

ArkTS不支持对象类型中包含call signature。改用类。

TypeScript

type DescribableFunction = {
    description: string
    (someArg: number): string // call signature
}

function doSomething(fn: DescribableFunction): void {
    console.log(fn.description + " returned " + fn(6))
}

ArkTS

class DescribableFunction {
    description: string
    public invoke(someArg: number): string {
        return someArg.toString()
    }
    constructor() {
        this.description = "desc"
    }
}

function doSomething(fn: DescribableFunction): void {
    console.log(fn.description + " returned " + fn.invoke(6))
}

doSomething(new DescribableFunction())

相关约束

  • 使用class而非具有构造签名的类型
使用class而非具有构造签名的类型

规则:arkts-no-ctor-signatures-type

级别:错误

ArkTS不支持对象类型中的构造签名。改用类。

TypeScript

class SomeObject {}

type SomeConstructor = {
    new (s: string): SomeObject
}

function fn(ctor: SomeConstructor) {
    return new ctor("hello")
}

ArkTS

class SomeObject {
    public f: string
    constructor (s: string) {
        this.f = s
    }
}

function fn(s: string): SomeObject {
    return new SomeObject(s)
}

相关约束

  • 使用class而非具有call signature的类型
仅支持一个静态块

规则:arkts-no-multiple-static-blocks

级别:错误

ArkTS不允许类中有多个静态块。如果存在多个静态块语句,请合并到一个静态块中。

TypeScript

class C {
    static s: string

    static {
        C.s = "aa"
    }
    static {
        C.s = C.s + "bb"
    }
}

ArkTS

class C {
    static s: string

    static {
        C.s = "aa"
        C.s = C.s + "bb"
    }
}
不支持index signature

规则:arkts-no-indexed-signatures

级别:错误

ArkTS不允许index signature。改用数组。

TypeScript

// 带index signature的接口:
interface StringArray {
    \[index: number\]: string
}

function getStringArray() : StringArray {
    return \["a", "b", "c"\]
}

const myArray: StringArray = getStringArray()
const secondItem = myArray\[1\]

ArkTS

class X {
    public f: string\[\] = \[\]
}

let myArray: X = new X()
const secondItem = myArray.f\[1\]
使用继承而非intersection type

规则:arkts-no-intersection-types

级别:错误

目前ArkTS不支持intersection type。可以使用继承作为替代方案。

TypeScript

interface Identity {
    id: number
    name: string
}

interface Contact {
    email: string
    phoneNumber: string
}

type Employee = Identity & Contact

ArkTS

interface Identity {
    id: number
    name: string
}

interface Contact {
    email: string
    phoneNumber: string
}

interface Employee extends Identity,  Contact {}
不支持this类型

规则:arkts-no-typing-with-this

级别:错误

ArkTS不支持this类型。改用显式具体类型。

TypeScript

interface ListItem {
    getHead(): this
}

class C {
    n: number = 0

    m(c: this) {
        console.log(c)
    }
}

ArkTS

interface ListItem {
    getHead(): ListItem
}

class C {
    n: number = 0

    m(c: C) {
        console.log(c)
    }
}
不支持条件类型

规则:arkts-no-conditional-types

级别:错误

ArkTS不支持条件类型别名。引入带显式约束的新类型,或使用Object重写逻辑。不支持infer关键字。

TypeScript

type X = T extends number ? T : never

type Y = T extends Array ? Item : never

ArkTS

// 在类型别名中提供显式约束
type X1 \= T

// 用Object重写,这种情况下,类型控制较少,需要更多的类型检查以确保安全
type X2 \= Object

// Item必须作为泛型参数使用,并能正确实例化
type YI> \= Item
不支持在constructor中声明字段

规则:arkts-no-ctor-prop-decls

级别:错误

ArkTS不支持在constructor中声明类字段。在class中声明这些字段。

TypeScript

class Person {
    constructor(
        protected ssn: string,
        private firstName: string,
        private lastName: string
    ) {
        this.ssn = ssn
        this.firstName = firstName
        this.lastName = lastName
    }

    getFullName(): string {
        return this.firstName + " " + this.lastName
    }
}

ArkTS

class Person {
    protected ssn: string
    private firstName: string
    private lastName: string

    constructor(ssn: string, firstName: string, lastName: string) {
        this.ssn = ssn
        this.firstName = firstName
        this.lastName = lastName
    }

    getFullName(): string {
        return this.firstName + " " + this.lastName
    }
}
接口中不支持构造签名

规则:arkts-no-ctor-signatures-iface

级别:错误

ArkTS不支持在接口中使用构造签名。改用函数或者方法。

TypeScript

interface I {
    new (s: string): I
}

function fn(i: I) {
    return new i("hello")
}

ArkTS

interface I {
    create(s: string): I
}

function fn(i: I) {
    return i.create("hello")
}

相关约束

  • 使用class而非具有构造签名的类型
不支持索引访问类型

规则:arkts-no-aliases-by-index

级别:错误

ArkTS不支持索引访问类型。改用类型别名。

TypeScript

type Point = {x: number, y: number}
type N = Point\["x"\] // 等同于number

ArkTS

class Point {x: number = 0; y: number = 0}
type N = number
不支持通过索引访问字段

规则:arkts-no-props-by-index

级别:错误

ArkTS不支持动态声明字段,不支持动态访问字段。只能访问已在类中声明或者继承可见的字段,访问其他字段将会造成编译时错误。
使用点操作符访问字段,例如(obj.field),不支持索引访问(obj[field])。
ArkTS支持通过索引访问TypedArray(例如Int32Array)中的元素。

TypeScript

class Point {
    x: number = 0
    y: number = 0
}
let p: Point = {x: 1, y: 2}
console.log(p\["x"\])

class Person {
    name: string = ""
    age: number = 0;
    \[key: string\]: string | number
}

let person: Person = {
    name: "John",
    age: 30,
    email: "\*\*\*@example.com",
    phoneNumber: "18\*\*\*\*\*\*\*\*\*",
}

ArkTS

class Point {
    x: number = 0
    y: number = 0
}
let p: Point = {x: 1, y: 2}
console.log(p.x)

class Person {
    name: string
    age: number
    email: string
    phoneNumber: string

    constructor(name: string, age: number, email: string,
                phoneNumber: string) {
        this.name = name
        this.age = age
        this.email = email
        this.phoneNumber = phoneNumber
    }
}

let person = new Person("John", 30, "\*\*\*@example.com", "18\*\*\*\*\*\*\*\*\*")
console.log(person\["name"\])         // 编译时错误
console.log(person.unknownProperty) // 编译时错误

let arr = new Int32Array(1)
console.log(arr\[0\])
不支持structural typing

规则:arkts-no-structural-typing

级别:错误

ArkTS不支持structural typing,编译器无法比较两种类型的publicAPI并决定它们是否相同。使用其他机制,例如继承、接口或类型别名。

TypeScript

interface I1 {
    f(): string
}

interface I2 { // I2等价于I1
    f(): string
}

class X {
    n: number = 0
    s: string = ""
}

class Y { // Y等价于X
    n: number = 0
    s: string = ""
}

let x = new X()
let y = new Y()

console.log("Assign X to Y")
y = x

console.log("Assign Y to X")
x = y

function foo(x: X) {
    console.log(x.n, x.s)
}

// 由于X和Y的API是等价的,所以X和Y是等价的
foo(new X())
foo(new Y())

ArkTS

interface I1 {
    f(): string
}

type I2 = I1 // I2是I1的别名

class B {
    n: number = 0
    s: string = ""
}

// D是B的继承类,构建了子类型和父类型的关系
class D extends B {
    constructor() {
        super()
    }
}

let b = new B()
let d = new D()

console.log("Assign D to B")
b = d // 合法赋值,因为B是D的父类

// 将b赋值给d将会引起编译时错误
// d = b

interface Z {
   n: number
   s: string
}

// 类X implements 接口Z,构建了X和Y的关系
class X implements Z {
    n: number = 0
    s: string = ""
}

// 类Y implements 接口Z,构建了X和Y的关系
class Y implements Z {
    n: number = 0
    s: string = ""
}

let x: Z = new X()
let y: Z = new Y()

console.log("Assign X to Y")
y = x // 合法赋值,它们是相同的类型

console.log("Assign Y to X")
x = y // 合法赋值,它们是相同的类型

function foo(c: Z): void {
    console.log(c.n, c.s)
}

// 类X和类Y implement 相同的接口,因此下面的两个函数调用都是合法的
foo(new X())
foo(new Y())
需要显式标注泛型函数类型实参

规则:arkts-no-inferred-generic-params

级别:错误

如果可以从传递给泛型函数的参数中推断出具体类型,ArkTS允许省略泛型类型实参。否则,省略泛型类型实参会发生编译时错误。禁止仅基于泛型函数返回类型推断泛型类型参数。

TypeScript

function choose(x: T, y: T): T {
    return Math.random() < 0.5 ? x : y
}

let x = choose(10, 20)   // OK,推断choose(...)
let y = choose("10", 20) // 编译时错误

function greet(): T {
    return "Hello" as T
}
let z = greet() // T的类型被推断为“unknown”

ArkTS

function choose(x: T, y: T): T {
    return Math.random() < 0.5 ? x : y
}

let x = choose(10, 20)   // OK,推断choose(...)
let y = choose("10", 20) // 编译时错误

function greet(): T {
    return "Hello" as T
}
let z = greet()
不支持使用正则字面量

规则:arkts-no-regexp-literals

级别:错误

当前ArkTS不支持正则字面量。请使用RegExp()创建正则对象。

TypeScript

let regex: RegExp = /bc\*d/

ArkTS

let regex: RegExp = new RegExp("/bc\*d/")
需要显式标注对象字面量的类型

规则:arkts-no-untyped-obj-literals

级别:错误

在ArkTS中,需要显式标注对象字面量的类型,否则,将发生编译时错误。在某些场景下,编译器可以根据上下文推断出字面量的类型。

在以下上下文中不支持使用字面量初始化类和接口:

  • 初始化具有anyObjectobject类型的任何对象
  • 初始化带有方法的类或接口
  • 初始化包含自定义含参数的构造函数的类
  • 初始化带readonly字段的类

TypeScript

let o1 = {n: 42, s: "foo"}
let o2: Object = {n: 42, s: "foo"}
let o3: object = {n: 42, s: "foo"}

let oo: Object\[\] = \[{n: 1, s: "1"}, {n: 2, s: "2"}\]

class C2 {
    s: string
    constructor(s: string) {
        this.s = "s =" + s
    }
}
let o4: C2 = {s: "foo"}

class C3 {
    readonly n: number = 0
    readonly s: string = ""
}
let o5: C3 = {n: 42, s: "foo"}

abstract class A {}
let o6: A = {}

class C4 {
    n: number = 0
    s: string = ""
    f() {
        console.log("Hello")
    }
}
let o7: C4 = {n: 42, s: "foo", f : () => {}}

class Point {
    x: number = 0
    y: number = 0
}

function id\_x\_y(o: Point): Point {
    return o
}

// TS支持structural typing,可以推断p的类型为Point
let p = {x: 5, y: 10}
id\_x\_y(p)

// 可通过上下文推断出对象字面量的类型为Point
id\_x\_y({x: 5, y: 10})

ArkTS

class C1 {
    n: number = 0
    s: string = ""
}

let o1: C1 = {n: 42, s: "foo"}
let o2: C1 = {n: 42, s: "foo"}
let o3: C1 = {n: 42, s: "foo"}

let oo: C1\[\] = \[{n: 1, s: "1"}, {n: 2, s: "2"}\]

class C2 {
    s: string
    constructor(s: string) {
        this.s = "s =" + s
    }
}
let o4 = new C2("foo")

class C3 {
    n: number = 0
    s: string = ""
}
let o5: C3 = {n: 42, s: "foo"}

abstract class A {}
class C extends A {}
let o6: C = {} // 或 let o6: C = new C()

class C4 {
    n: number = 0
    s: string = ""
    f() {
        console.log("Hello")
    }
}
let o7 = new C4()
o7.n = 42
o7.s = "foo"

class Point {
    x: number = 0
    y: number = 0

    // 在字面量初始化之前,使用constructor()创建一个有效对象。
    // 由于没有为Point定义构造函数,编译器将自动添加一个默认构造函数。
}

function id\_x\_y(o: Point): Point {
    return o
}

// 字面量初始化需要显式定义类型
let p: Point = {x: 5, y: 10}
id\_x\_y(p)

// id\_x\_y接受Point类型,字面量初始化生成一个Point的新实例
id\_x\_y({x: 5, y: 10})

相关约束

  • 对象字面量不能用于类型声明
  • 数组字面量必须仅包含可推断类型的元素

相关约束

  • 对象字面量不能用于类型声明
  • 数组字面量必须仅包含可推断类型的元素
对象字面量不能用于类型声明

规则:arkts-no-obj-literals-as-types

级别:错误

ArkTS不支持使用对象字面量声明类型,可以使用类或者接口声明类型。

TypeScript

let o: {x: number, y: number} = {
    x: 2,
    y: 3
}

type S = Set<{x: number, y: number}>

ArkTS

class O {
    x: number = 0
    y: number = 0
}

let o: O = {x: 2, y: 3}

type S = Set

相关约束

  • 对象字面量必须对应某些显式声明的类或接口
  • 数组字面量必须仅包含可推断类型的元素
数组字面量必须仅包含可推断类型的元素

规则:arkts-no-noninferrable-arr-literals

级别:错误

本质上,ArkTS将数组字面量的类型推断为数组所有元素的联合类型。如果其中任何一个元素的类型无法根据上下文推导出来(例如,无类型的对象字面量),则会发生编译时错误。

TypeScript

let a = \[{n: 1, s: "1"}, {n: 2, s : "2"}\]

ArkTS

class C {
    n: number = 0
    s: string = ""
}

let a1 = \[{n: 1, s: "1"} as C, {n: 2, s : "2"} as C\] // a1的类型为“C\[\]”
let a2: C\[\] = \[{n: 1, s: "1"}, {n: 2, s : "2"}\]      // a2的类型为“C\[\]”

相关约束

  • 对象字面量必须对应某些显式声明的类或接口
  • 对象字面量不能用于类型声明
使用箭头函数而非函数表达式

规则:arkts-no-func-expressions

级别:错误

ArkTS不支持函数表达式。使用箭头函数。

TypeScript

let f = function (s: string) {
    console.log(s)
}

ArkTS

let f = (s: string) => {
    console.log(s)
}
使用泛型函数而非泛型箭头函数

规则:arkts-no-generic-lambdas

级别:错误

ArkTS不支持泛型箭头函数。

TypeScript

let generic\_arrow\_func =  (x: T) => { return x }

generic\_arrow\_func("string")

ArkTS

function generic\_func(x: T): T {
    return x
}

generic\_func("string")
不支持使用类表达式

规则:arkts-no-class-literals

级别:错误

ArkTS不支持使用类表达式。必须显式声明一个类。

TypeScript

const Rectangle = class {
    constructor(height: number, width: number) {
        this.height = height
        this.width = width
    }

    height
    width
}

const rectangle = new Rectangle(0.0, 0.0)

ArkTS

class Rectangle {
    constructor(height: number, width: number) {
        this.height = height
        this.width = width
    }

    height: number
    width: number
}

const rectangle = new Rectangle(0.0, 0.0)
类不允许被implements

规则:arkts-implements-only-iface

级别:错误

ArkTS不允许类被implements,只有接口可以被implements

TypeScript

class C {
  foo() {}
}

class C1 implements C {
  foo() {}
}

ArkTS

interface C {
  foo(): void
}

class C1 implements C {
  foo() {}
}
不支持修改对象的方法

规则:arkts-no-method-reassignment

级别:错误

ArkTS不支持修改对象的方法。在静态语言中,对象的布局是确定的。一个类的所有对象实例享有同一个方法。
如果需要为某个特定的对象增加方法,可以封装函数或者使用继承的机制。

TypeScript

class C {
    foo() {
        console.log("foo")
    }
}

function bar() {
    console.log("bar")
}

let c1 = new C()
let c2 = new C()
c2.foo = bar

c1.foo() // foo
c2.foo() // bar

ArkTS

class C {
    foo() {
        console.log("foo")
    }
}

class Derived extends C {
    foo() {
        console.log("Extra")
        super.foo()
    }
}

function bar() {
    console.log("bar")
}

let c1 = new C()
let c2 = new C()
c1.foo() // foo
c2.foo() // foo

let c3 = new Derived()
c3.foo() // Extra foo
类型转换仅支持as T语法

规则:arkts-as-casts

级别:错误

在ArkTS中,as关键字是类型转换的唯一语法。错误的类型转换会导致编译时错误或者运行时抛出ClassCastException异常。不支持使用语法进行类型转换。

当需要将primitive类型(如numberboolean)转换成引用类型时,请使用new表达式。

TypeScript

class Shape {}
class Circle extends Shape {x: number = 5}
class Square extends Shape {y: string = "a"}

function createShape(): Shape {
    return new Circle()
}

let c1 =  createShape()

let c2 = createShape() as Circle

// 如果转换错误,不会产生编译时或运行时报错
let c3 = createShape() as Square
console.log(c3.y) // undefined

// 在TS中,由于\`as\`关键字不会在运行时生效,所以\`instanceof\`的左操作数不会在运行时被装箱成引用类型
let e1 = (5.0 as Number) instanceof Number // false

// 创建Number对象,获得预期结果:
let e2 = (new Number(5.0)) instanceof Number // true

ArkTS

class Shape {}
class Circle extends Shape {x: number = 5}
class Square extends Shape {y: string = "a"}

function createShape(): Shape {
    return new Circle()
}

let c2 = createShape() as Circle

// 运行时抛出ClassCastException异常:
let c3 = createShape() as Square

// 创建Number对象,获得预期结果:
let e2 = (new Number(5.0)) instanceof Number // true
不支持JSX表达式

规则:arkts-no-jsx

级别:错误

不支持使用JSX。

一元运算符+-~仅适用于数值类型

规则:arkts-no-polymorphic-unops

级别:错误

ArkTS仅允许一元运算符用于数值类型,否则会发生编译时错误。与TypeScript不同,ArkTS不支持隐式将字符串转换成数值,必须进行显式转换。

TypeScript

let a = +5        // 5(number类型)
let b = +"5"      // 5(number类型)
let c = \-5        // -5(number类型)
let d = -"5"      // -5(number类型)
let e = ~5        // -6(number类型)
let f = ~"5"      // -6(number类型)
let g = +"string" // NaN(number类型)

function returnTen(): string {
    return "-10"
}

function returnString(): string {
    return "string"
}

let x = +returnTen()    // -10(number类型)
let y = +returnString() // NaN

ArkTS

let a = +5        // 5(number类型)
let b = +"5"      // 编译时错误
let c = \-5        // -5(number类型)
let d = -"5"      // 编译时错误
let e = ~5        // -6(number类型)
let f = ~"5"      // 编译时错误
let g = +"string" // 编译时错误

function returnTen(): string {
    return "-10"
}

function returnString(): string {
    return "string"
}

let x = +returnTen()    // 编译时错误
let y = +returnString() // 编译时错误
不支持delete运算符

规则:arkts-no-delete

级别:错误

ArkTS中,对象布局在编译时就确定了,且不能在运行时被更改。因此,删除属性的操作没有意义。

TypeScript

class Point {
    x?: number = 0.0
    y?: number = 0.0
}

let p = new Point()
delete p.y

ArkTS

// 可以声明一个可空类型并使用null作为缺省值
class Point {
    x: number | null = 0
    y: number | null = 0
}

let p = new Point()
p.y = null

相关约束

  • 对象的属性名必须是合法的标识符
  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 仅允许在表达式中使用typeof运算符
  • 不支持in运算符
  • 禁止运行时检查对象属性
仅允许在表达式中使用typeof运算符

规则:arkts-no-type-query

级别:错误

ArkTS仅支持在表达式中使用typeof运算符。不允许使用typeof作为类型。

TypeScript

let n1 = 42
let s1 = "foo"
console.log(typeof n1) // "number"
console.log(typeof s1) // "string"
let n2: typeof n1
let s2: typeof s1

ArkTS

let n1 = 42
let s1 = "foo"
console.log(typeof n1) // "number"
console.log(typeof s1) // "string"
let n2: number
let s2: string

相关约束

  • 对象的属性名必须是合法的标识符
  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 不支持delete运算符
  • 不支持in运算符
  • 禁止运行时检查对象属性
  • 限制使用标准库
部分支持instanceof运算符

规则:arkts-instanceof-ref-types

级别:错误

在TypeScript中,instanceof运算符的左操作数的类型必须为any类型、对象类型,或者它是类型参数,否则结果为false。在ArkTS中,instanceof运算符的左操作数的类型必须为引用类型,否则会发生编译时错误。此外,在ArkTS中,instanceof运算符的左操作数不能是类型。

TypeScript

class X {
    // ...
}

let a = (new X()) instanceof Object // true
let b = (new X()) instanceof X      // true
let c = X instanceof Object         // true, 左操作数是一个类型
let d = X instanceof X              // false, 左操作数是一个类型

ArkTS

class X {
    // ...
}

let a = (new X()) instanceof Object // true
let b = (new X()) instanceof X      // true
let c = X instanceof Object         // 编译时错误, 左操作数是一个类型
let d = X instanceof X              // 编译时错误, 左操作数是一个类型
不支持in运算符

规则:arkts-no-in

级别:错误

由于在ArkTS中,对象布局在编译时是已知的并且在运行时无法修改,因此,不支持in运算符。如果仍需检查某些类成员是否存在,使用instanceof代替。

TypeScript

class Person {
    name: string = ""
}
let p = new Person()

let b = "name" in p // true

ArkTS

class Person {
    name: string = ""
}
let p = new Person()

let b = p instanceof Person // true,且属性name一定存在

相关约束

  • 对象的属性名必须是合法的标识符
  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 不支持delete运算符
  • 仅允许在表达式中使用typeof运算符
  • 禁止运行时检查对象属性
  • 限制使用标准库
不支持解构赋值

规则:arkts-no-destruct-assignment

级别:错误

ArkTS不支持解构赋值。可使用其他替代方法,例如,使用临时变量。

TypeScript

let \[one, two\] = \[1, 2\]; // 此处需要分号
\[one, two\] = \[two, one\]

let head, tail
\[head, ...tail\] = \[1, 2, 3, 4\]

ArkTS

let arr: number\[\] = \[1, 2\]
let one = arr\[0\]
let two = arr\[1\]

let tmp = one
one = two
two = tmp

let data: Number\[\] = \[1, 2, 3, 4\]
let head = data\[0\]
let tail: Number\[\] = \[\]
for (let i = 1; i < data.length; ++i) {
    tail.push(data\[i\])
}
逗号运算符,仅用在for循环语句中

规则:arkts-no-comma-outside-loops

级别:错误

为了方便理解执行顺序,在ArkTS中,逗号运算符,仅适用于for循环语句中。

TypeScript

for (let i = 0, j = 0; i < 10; ++i, j += 2) {
    console.log(i)
    console.log(j)
}

let x = 0
x = (++x, x++) // 1

ArkTS

for (let i = 0, j = 0; i < 10; ++i, j += 2) {
    console.log(i)
    console.log(j)
}

// 通过语句表示执行顺序,而非逗号运算符
let x = 0
++x
x = x++
不支持解构变量声明

规则:arkts-no-destruct-decls

级别:错误

ArkTS不支持解构变量声明。它是一个依赖于结构兼容性的动态特性并且解构声明中的名称必须和被解构对象中的属性名称一致。

TypeScript

class Point {
    x: number = 0.0
    y: number = 0.0
}

function returnZeroPoint(): Point {
    return new Point()
}

let {x, y} = returnZeroPoint()

ArkTS

class Point {
    x: number = 0.0
    y: number = 0.0
}

function returnZeroPoint(): Point {
    return new Point()
}

// 创建一个局部变量来处理每个字段:
let zp = returnZeroPoint()
let x = zp.x
let y = zp.y
不支持在catch语句标注类型

规则:arkts-no-types-in-catch

级别:错误

在TypeScript的catch语句中,只能标注anyunknown类型。由于ArkTS不支持这些类型,应省略类型标注。

TypeScript

try {
    // 一些代码
}
catch (a: unknown) {
    // 处理异常
}

ArkTS

try {
    // 一些代码
}
catch (a) {
    // 处理异常
}

相关约束

  • 限制throw语句中表达式的类型
不支持for .. in

规则:arkts-no-for-in

级别:错误

由于在ArkTS中,对象布局在编译时是确定的、并且不能在运行时被改变,所以不支持使用for .. in迭代一个对象的属性。对于数组来说,可以使用常规的for循环。

TypeScript

let a: number\[\] = \[1.0, 2.0, 3.0\]
for (let i in a) {
    console.log(a\[i\])
}

ArkTS

let a: number\[\] = \[1.0, 2.0, 3.0\]
for (let i = 0; i < a.length; ++i) {
    console.log(a\[i\])
}

相关约束

  • for-of仅适用于数组和字符串
for-of仅适用于数组和字符串

规则:arkts-for-of-str-arr

级别:错误

ArkTS支持通过for .. of迭代数组、字符串和TypedArray(例如Int32Array),但不支持迭代对象。

TypeScript

let a: Set = new Set(\[1, 2, 3\])
for (let s of a) {
    console.log(s)
}

ArkTS

let a: Set = new Set(\[1, 2, 3\])
let numbers = Array.from(a.values())
for (let n of numbers) {
    console.log(n)
}

相关约束

  • 不支持for … in
不支持映射类型

规则:arkts-no-mapped-types

级别:错误

ArkTS不支持映射类型。使用其他语法来表示相同的语义。

TypeScript

type OptionsFlags = {
    \[Property in keyof Type\]: boolean
}

ArkTS

class C {
    n: number = 0
    s: string = ""
}

class CFlags {
    n: boolean = false
    s: boolean = false
}

相关约束

  • 不支持keyof运算符
不支持with语句

规则:arkts-no-with

级别:错误

ArkTS不支持with语句。使用其他语法来表示相同的语义。

TypeScript

with (Math) { // 编译时错误, 但是仍能生成JavaScript代码
    let r: number = 42
    console.log("Area: ", PI \* r \* r)
}

ArkTS

let r: number = 42
console.log("Area: ", Math.PI \* r \* r)
限制throw语句中表达式的类型

规则:arkts-limited-throw

级别:错误

ArkTS只支持抛出Error类或其派生类的实例。禁止抛出其他类型(例如numberstring)的数据。

TypeScript

throw 4
throw ""
throw new Error()

ArkTS

throw new Error()
限制省略函数返回类型标注

规则:arkts-no-implicit-return-types

级别:错误

ArkTS在部分场景中支持对函数返回类型进行推断。当return语句中的表达式是对某个函数或方法进行调用,且该函数或方法的返回类型没有被显著标注时,会出现编译时错误。在这种情况下,请标注函数返回类型。

TypeScript

// 只有在开启noImplicitAny选项时会产生编译时错误
function f(x: number) {
    if (x <= 0) {
        return x
    }
    return g(x)
}

// 只有在开启noImplicitAny选项时会产生编译时错误
function g(x: number) {
    return f(x - 1)
}

function doOperation(x: number, y: number) {
    return x + y
}

console.log(f(10))
console.log(doOperation(2, 3))

ArkTS

// 需标注返回类型:
function f(x: number) : number {
    if (x <= 0) {
        return x
    }
    return g(x)
}

// 可以省略返回类型,返回类型可以从f的类型标注推导得到
function g(x: number) : number {
    return f(x - 1)
}

// 可以省略返回类型
function doOperation(x: number, y: number) {
    return x + y
}

console.log(f(10))
console.log(doOperation(2, 3))
不支持参数解构的函数声明

规则:arkts-no-destruct-params

级别:错误

ArkTS要求实参必须直接传递给函数,且必须指定到形参。

TypeScript

function drawText({ text = "", location: \[x, y\] = \[0, 0\], bold = false }) {
    console.log(text)
    console.log(x)
    console.log(y)
    console.log(bold)
}

drawText({ text: "Hello, world!", location: \[100, 50\], bold: true })

ArkTS

function drawText(text: String, location: number\[\], bold: boolean) {
    let x = location\[0\]
    let y = location\[1\]
    console.log(text)
    console.log(x)
    console.log(y)
    console.log(bold)
}

function main() {
    drawText("Hello, world!", \[100, 50\], true)
}
不支持在函数内声明函数

规则:arkts-no-nested-funcs

级别:错误

ArkTS不支持在函数内声明函数。改用lambda函数。

TypeScript

function addNum(a: number, b: number): void {

    // 函数内声明函数:
    function logToConsole(message: String): void {
        console.log(message)
    }

    let result = a + b

    // 调用函数:
    logToConsole("result is " + result)
}

ArkTS

function addNum(a: number, b: number): void {
    // 使用lambda函数代替声明函数:
    let logToConsole: (message: string) => void = (message: string): void => {
        console.log(message)
    }

    let result = a + b

    logToConsole("result is " + result)
}
不支持在函数和类的静态方法中使用this

规则:arkts-no-standalone-this

级别:错误

ArkTS不支持在函数和类的静态方法中使用this。只能在类的实例方法中使用this

TypeScript

function foo(i: number) {
    this.count = i // 只有在开启noImplicitThis选项时会产生编译时错误
}

class A {
    count: number = 1
    m = foo
}

let a = new A()
console.log(a.count) // 打印“1”
a.m(2)
console.log(a.count) // 打印“2”

ArkTS

class A {
    count: number = 1
    m(i: number): void {
        this.count = i
    }
}

function main(): void {
    let a = new A()
    console.log(a.count)  // 打印"1"
    a.m(2)
    console.log(a.count)  // 打印"2"
}

相关约束

  • 不支持Function.apply、Function.bind以及Function.call
不支持生成器函数

规则:arkts-no-generators

级别:错误

目前ArkTS不支持生成器函数。使用asyncawait机制进行并行任务处理。

TypeScript

function\* counter(start: number, end: number) {
    for (let i = start; i <= end; i++) {
        yield i
    }
}

for (let num of counter(1, 5)) {
    console.log(num)
}

ArkTS

async function complexNumberProcessing(n : number) : Promise {
    //一些代码逻辑
    return n
}

async function foo() {
    for (let i = 1; i <= 5; i++) {
        console.log(await complexNumberProcessing(i))
    }
}

foo()
使用instanceofas进行类型保护

规则:arkts-no-is

级别:错误

ArkTS不支持is运算符,必须用instanceof运算符替代。在使用之前,必须使用as运算符将对象转换为需要的类型。

TypeScript

class Foo {
    foo: number = 0
    common: string = ""
}

class Bar {
    bar: number = 0
    common: string = ""
}

function isFoo(arg: any): arg is Foo {
    return arg.foo !== undefined
}

function doStuff(arg: Foo | Bar) {
    if (isFoo(arg)) {
        console.log(arg.foo)    // OK
        console.log(arg.bar)    // 编译时错误
    } else {
        console.log(arg.foo)    // 编译时错误
        console.log(arg.bar)    // OK
    }
}

doStuff({ foo: 123, common: '123' })
doStuff({ bar: 123, common: '123' })

ArkTS

class Foo {
    foo: number = 0
    common: string = ""
}

class Bar {
    bar: number = 0
    common: string = ""
}

function isFoo(arg: Object): boolean {
    return arg instanceof Foo
}

function doStuff(arg: Object): void {
    if (isFoo(arg)) {
        let fooArg = arg as Foo
        console.log(fooArg.foo)     // OK
        console.log(arg.bar)        // 编译时错误
    } else {
        let barArg = arg as Bar
        console.log(arg.foo)        // 编译时错误
        console.log(barArg.bar)     // OK
    }
}

function main(): void {
    doStuff(new Foo())
    doStuff(new Bar())
}
不支持keyof运算符

规则:arkts-no-keyof

级别:错误

在ArkTS中,由于对象布局在编译时就确定了,且不能在运行时被更改,因此,不支持使用keyof运算符。直接访问对象的属性。

TypeScript

class Point {
    x: number = 1
    y: number = 2
}

type PointKeys = keyof Point  // PointKeys表示Point属性名称组成的联合类型

function getPropertyValue(obj: Point, key: PointKeys) {
    return obj\[key\]
}

let obj = new Point()
console.log(getPropertyValue(obj, "x"))  // 打印"1"
console.log(getPropertyValue(obj, "y"))  // 打印"2"

ArkTS

class Point {
    x: number = 1
    y: number = 2
}

function getPropertyValue(obj: Point, key: string): number {
    if (key == "x") {
        return obj.x
    }
    if (key == "y") {
        return obj.y
    }
    throw new Error()  // 处理没有该属性的分支
    return 0
}

function main(): void {
    let obj = new Point()
    console.log(getPropertyValue(obj, "x"))  // 打印"1"
    console.log(getPropertyValue(obj, "y"))  // 打印"2"
}
展开运算符仅支持函数剩余参数为数组类型

规则:arkts-no-spread

级别:错误

展开运算符唯一支持的场景是函数剩余参数为数组类型,包括TypedArray(例如Int32Array)。

TypeScript

function foo(x : number, y : number, z : number) {
    console.log(x, y, z)
}

let args : \[number, number, number\] = \[0, 1, 2\]
foo(...args)

let list1 = \[1, 2\]
let list2 = \[...list1, 3, 4\]

let point2d = {x: 1, y: 2}
let point3d = {...point2d, z: 3}

ArkTS

function sum\_numbers(...numbers: number\[\]): number {
    let res = 0
    for (let n of numbers)
        res += n
    return res
}
console.log(sum\_numbers(1, 2, 3))

function log\_numbers(x : number, y : number, z : number) {
    console.log(x, y, z)
}

let numbers: number\[\] = \[1, 2, 3\]
log\_numbers(numbers\[0\], numbers\[1\], numbers\[2\])

let list1 : number\[\] = \[1, 2\]
let list2 : number\[\] = \[list1\[0\], list1\[1\], 3, 4\]

class Point2D {
    x: number = 0; y: number = 0
}

class Point3D {
    x: number = 0; y: number = 0; z: number = 0
    constructor(p2d: Point2D, z: number) {
        this.x = p2d.x
        this.y = p2d.y
        this.z = z
    }
}

let p3d = new Point3D({x: 1, y: 2} as Point2D, 3)
console.log(p3d.x, p3d.y, p3d.z)
接口不能继承具有相同属性的两个接口

规则:arkts-no-extend-same-prop

级别:错误

在TypeScript中,如果一个接口继承了具有相同方法的两个接口,则该接口必须使用联合类型来声明该方法。在ArkTS中,由于一个接口中不能包含两个无法区分的方法(例如两个参数列表相同但返回类型不同的方法),因此,接口不能继承具有相同属性的两个接口。

TypeScript

interface Mover {
    getStatus(): { speed: number }
}
interface Shaker {
    getStatus(): { frequency: number }
}

interface MoverShaker extends Mover, Shaker {
    getStatus(): {
        speed: number
        frequency: number
    }
}

class C implements MoverShaker {
    private speed: number = 0
    private frequency: number = 0

    getStatus() {
        return { speed: this.speed, frequency: this.frequency }
    }
}

ArkTS

class MoveStatus {
    public speed : number
    constructor() {
        this.speed = 0
    }
}
interface Mover {
    getMoveStatus(): MoveStatus
}

class ShakeStatus {
    public frequency : number
    constructor() {
        this.frequency = 0
    }
}
interface Shaker {
    getShakeStatus(): ShakeStatus
}

class MoveAndShakeStatus {
    public speed : number
    public frequency : number
    constructor() {
        this.speed = 0
        this.frequency = 0
    }
}

class C implements Mover, Shaker {
    private move\_status : MoveStatus
    private shake\_status : ShakeStatus

    constructor() {
        this.move\_status = new MoveStatus()
        this.shake\_status = new ShakeStatus()
    }

    public getMoveStatus() : MoveStatus {
        return this.move\_status
    }

    public getShakeStatus() : ShakeStatus {
        return this.shake\_status
    }

    public getStatus(): MoveAndShakeStatus {
        return {
            speed: this.move\_status.speed,
            frequency: this.shake\_status.frequency
        }
    }
}
不支持声明合并

规则:arkts-no-decl-merging

级别:错误

ArkTS不支持类、接口的声明合并。

TypeScript

interface Document {
    createElement(tagName: any): Element
}

interface Document {
    createElement(tagName: string): HTMLElement
}

interface Document {
    createElement(tagName: number): HTMLDivElement
    createElement(tagName: boolean): HTMLSpanElement
    createElement(tagName: string, value: number): HTMLCanvasElement
}

ArkTS

interface Document {
    createElement(tagName: number): HTMLDivElement
    createElement(tagName: boolean): HTMLSpanElement
    createElement(tagName: string, value: number): HTMLCanvasElement
    createElement(tagName: string): HTMLElement
    createElement(tagName: Object): Element
}
接口不能继承类

规则:arkts-extends-only-class

级别:错误

ArkTS不支持接口继承类。接口只能继承接口。

TypeScript

class Control {
    state: number = 0
}

interface SelectableControl extends Control {
    select(): void
}

ArkTS

interface Control {
    state: number
}

interface SelectableControl extends Control {
    select(): void
}
禁止运行时检查对象属性

规则:arkts-no-prop-existence-check

级别:错误

ArkTS中,对象布局在编译时就确定了,且不能在运行时被更改。禁止运行时检查对象属性。使用as运算符进行类型转换以访问相应的属性和方法。访问对象中不存在的属性将导致编译时错误。

TypeScript

class A {
    foo() {}
    bar() {}
}

function getSomeObject() {
    return new A()
}

let obj: any = getSomeObject()
if (obj && obj.foo && obj.bar) {
    console.log("Yes")  // 此示例中将打印 "Yes"
} else {
    console.log("No")
}

ArkTS

class A {
    foo(): void {}
    bar(): void {}
}

function getSomeObject(): A {
    return new A()
}

function main(): void {
    let tmp: Object = getSomeObject()
    let obj: A = tmp as A
    obj.foo()       // OK
    obj.bar()       // OK
    obj.some\_foo()  // 编译时错误:方法some\_foo不存在于此类型上
}

相关约束

  • 对象的属性名必须是合法的标识符
  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 不支持delete运算符
  • 仅允许运算符typeof在表达式上下文中使用
  • 不支持in运算符
  • 限制使用标准库
不支持构造函数类型

规则:arkts-no-ctor-signatures-funcs

级别:错误

ArkTS不支持使用构造函数类型。改用lambda函数。

TypeScript

class Person {
    constructor( name: string,
        age: number ) {}
}
type PersonCtor = new (name: string, age: number) => Person

function createPerson(Ctor: PersonCtor, name: string, age: number): Person
{
    return new Ctor(name, age)
}

const person = createPerson(Person, 'John', 30)

ArkTS

class Person {
    constructor( name: string,
        age: number ) {}
}
type PersonCtor = (n: string, a: number) => Person

function createPerson(Ctor: PersonCtor, n: string, a: number): Person {
    return Ctor(n, a)
}

let Impersonizer: PersonCtor = (n: string, a: number): Person => {
    return new Person(n, a)
}

const person = createPerson(Impersonizer, "John", 30)
只能使用类型相同的编译时表达式初始化枚举成员

规则:arkts-no-enum-mixed-types

级别:错误

ArkTS不支持使用在运行期间才能计算的表达式来初始化枚举成员。此外,枚举中所有显式初始化的成员必须具有相同的类型。

TypeScript

enum E1 {
    A = 0xa,
    B = 0xb,
    C = Math.random(),
    D = 0xd,
    E // 推断出0xe
}

enum E2 {
    A = 0xa,
    B = "0xb",
    C = 0xc,
    D = "0xd"
}

ArkTS

enum E1 {
    A \= 0xa,
    B \= 0xb,
    C \= 0xc,
    D \= 0xd,
    E // 推断出0xe
}

enum E2 {
    A \= "0xa",
    B \= "0xb",
    C \= "0xc",
    D \= "0xd"
}
不支持enum声明合并

规则:arkts-no-enum-merging

级别:错误

ArkTS不支持enum声明合并。

TypeScript

enum Color {
    RED,
    GREEN
}
enum Color {
    YELLOW = 2
}
enum Color {
    BLACK = 3,
    BLUE
}

ArkTS

enum Color {
 RED,
 GREEN,
 YELLOW,
 BLACK,
    BLUE
}
命名空间不能被用作对象

规则:arkts-no-ns-as-obj

级别:错误

ArkTS不支持将命名空间用作对象。可以使用类或模块。

TypeScript

namespace MyNamespace {
    export let x: number
}

let m = MyNamespace
m.x = 2

ArkTS

namespace MyNamespace {
    export let x: number
}

MyNamespace.x = 2
不支持命名空间中的非声明语句

规则:arkts-no-ns-statements

级别:错误

ArkTS不支持命名空间中的非声明语句。可以将非声明语句写在函数中。

TypeScript

namespace A {
    export let x: number
    x = 1
}

ArkTS

namespace A {
    export let x: number

    export function init() {
      x = 1
    }
}

// 调用初始化函数来执行:
A.init()
不支持import type

规则:arkts-no-special-imports

级别:错误

ArkTS不支持import type。改为import

TypeScript

// 通用导入语法
import { APIResponseType } from "api"

// 导入类型
import type { APIResponseType } from "api"

ArkTS

import { APIResponseType } from "api"

相关约束

  • 不支持仅为副作用而导入一个模块
  • 不支持import default as …
  • 不支持require和import赋值表达式
不支持仅为副作用而导入一个模块

规则:arkts-no-side-effects-imports

级别:错误

ArkTS不支持window等全局变量,避免模块导入时产生副作用。可以通过*语法获取所有导出的变量。

TypeScript

// \=\=\= “path/to/module.ts”中的模块
export const EXAMPLE\_VALUE \= 42

// 设置全局变量
window.MY\_GLOBAL\_VAR \= "Hello, world!"

//\=\=\=\= 使用此模块:
import "path/to/module"

ArkTS

import \* as ns from "path/to/module"
不支持import default as ...

规则:arkts-no-import-default-as

级别:错误

ArkTS不支持import default as ...语法。使用显式的import ... from ...语法。

TypeScript

import { default as d } from "mod"

ArkTS

import d from "mod"
不支持requireimport赋值表达式

规则:arkts-no-require

级别:错误

ArkTS不支持通过require导入,也不支持import赋值表达式。改用import

TypeScript

import m = require("mod")

ArkTS

import \* as m from "mod"

相关约束

  • 不支持export = …语法
部分支持重导出

规则:arkts-limited-reexport

级别:错误

ArkTS支持命名重导出和重命名重导出。支持export * ...的语法,不支持export * as ...的语法。

TypeScript

// module1
export class Class1 {
    // ...
}
export class Class2 {
    // ...
}

// module2
export \* as utilities from "module1"

// consumer模块
import { utilities } from "module2"

const myInstance = new MyClass()

ArkTS

// module1
export class Class1 {
    // ...
}
export class Class2 {
    // ...
}

// module2
export { Class1 } from "module1"
export { C2 as Class2 } from "module1"

// 支持以下语法
// export \* from "module1"

// consumer模块
import { Class1, Class2 } from "module2"

const myInstance = new MyClass()

相关约束

  • 不支持export = …语法
不支持export = ...语法

规则:arkts-no-export-assignment

级别:错误

ArkTS不支持export = ...语法。改用常规的exportimport

TypeScript

// module1
export = Point

class Point {
    constructor(x: number, y: number) {}
    static origin = new Point(0, 0)
}

// module2
import Pt = require("module1")

let p = Pt.origin

ArkTS

// module1
export class Point {
    constructor(x: number, y: number) {}
    static origin = new Point(0, 0)
}

// module2
import \* as Pt from "module1"

let p = Pt.origin

相关约束

  • 不支持require和import赋值表达式
  • 部分支持重导出
不支持export type

规则:arkts-no-special-exports

级别:错误

ArkTS不支持export type。改用export

TypeScript

// 显式导出class:
export class Class1 {
    // ...
}

// 声明一个类,之后通过\`export type\`导出
class Class2 {
    // ...
}

// 不支持
export type { Class2 }

ArkTS

// 显式导出class:
export class Class1 {
    // ...
}

// 显式导出class:
export class Class2 {
    // ...
}
不支持ambient module声明

规则:arkts-no-ambient-decls

级别:错误

ArkTS不支持ambient module声明,因为ArkTS本身有与JavaScript交互的机制。

TypeScript

declare module "someModule" {
    export function normalize(s : string) : string;
}

ArkTS

// 从原始模块中导入需要的内容
import { normalize } from "someModule"

相关约束

  • 不支持在模块名中使用通配符
  • 模块标识符中不允许使用.js扩展名
不支持在模块名中使用通配符

规则:arkts-no-module-wildcards

级别:错误

由于在ArkTS中,导入是编译时而非运行时行为,因此,不支持在模块名中使用通配符。

TypeScript

// 声明
declare module "\*!text" {
    const content: string
    export default content
}

import fileContent from "some.txt!text"

ArkTS

// 声明
declare namespace N {
    function foo(x: number): number
}

import \* as m from "module"
console.log("N.foo called: ", N.foo(42))

相关约束

  • 不支持ambient module声明
  • 不支持通用模块定义(UMD)
  • 模块标识符中不允许使用.js扩展名
不支持通用模块定义(UMD)

规则:arkts-no-umd

级别:错误

ArkTS不支持通用模块定义(UMD)。因为在ArkTS中没有“脚本”的概念(相对于“模块”)。此外,在ArkTS中,导入是编译时而非运行时特性。改用exportimport语法。

TypeScript

// math-lib.d.ts
export const isPrime(x: number): boolean
export as namespace mathLib

// 脚本中
mathLib.isPrime(2)

ArkTS

// math-lib.d.ts
namespace mathLib {
    export isPrime(x: number): boolean
}

// 程序中
import { mathLib } from "math-lib"
mathLib.isPrime(2)

相关约束

  • 不支持在模块名中使用通配符
模块标识符中不允许使用.js扩展名

规则:arkts-no-js-extension

级别:错误

ArkTS不允许在模块标识符中使用.js扩展名,因为ArkTS本身有与JavaScript交互的机制。

TypeScript

import { something } from "module.js"

ArkTS

import { something } from "module"

相关约束

  • 不支持ambient module声明
  • 不支持在模块名中使用通配符
不支持new.target

规则:arkts-no-new-target

级别:错误

ArkTS没有原型的概念,因此不支持new.target。此特性不符合静态类型的原则。

TypeScript

class CustomError extends Error {
    constructor(message?: string) {
        // 'Error'在此处断开原型链
        super(message)

        // 恢复原型链
        Object.setPrototypeOf(this, new.target.prototype)
    }
}

ArkTS

class CustomError extends Error {
    constructor(message?: string) {
        // 调用父类构造函数,继承链是静态的,且不能在运行时被改变
        super(message)
        console.log(this instanceof Error) // true
    }
}
let ce = new CustomError()

相关约束

  • 不支持在原型上赋值
不支持确定赋值断言

规则:arkts-no-definite-assignment

级别:错误

ArkTS不支持确定赋值断言let v!: T,这是一种过度的编译器提示。改用带初始化的声明。

TypeScript

let x!: number // 提示:在使用前将x初始化

initialize()

function initialize() {
    x = 10
}

console.log("x = " + x)

ArkTS

function initialize() : number {
    return 10
}

let x: number = initialize()

console.log("x = " + x)
不支持IIFE(立即调用函数表达式)作为命名空间的声明

规则:arkts-no-iife

级别:错误

由于在ArkTS中,匿名函数不能作为命名空间,因此不支持IIFE作为命名空间的声明。请使用命名空间的常规语法。

TypeScript

const C = (function () {
    class Cl {
        static static\_value = "static\_value";
        static any\_value: any = "any\_value";
        string\_field = "string\_field";
    }

    return Cl;
})();

C.prop = 2;

ArkTS

namespace C {
    // ...
}
不支持在原型上赋值

规则:arkts-no-prototype-assignment

级别:错误

ArkTS没有原型的概念,因此不支持在原型上赋值。此特性不符合静态类型的原则。

TypeScript

var C = function(p) {
    this.p = p // 只有在开启noImplicitThis选项时会产生编译时错误
}

C.prototype = {
    m() {
        console.log(this.p)
    }
}

C.prototype.q = function(r: number) {
    return this.p == r
}

ArkTS

class C {
    p: number = 0
    m() {
        console.log(this.p)
    }
    q(r: number) {
        return this.p == r
    }
}

相关约束

  • 不支持new.target
不支持globalThis

规则:arkts-no-globalthis

级别:错误

由于ArkTS不支持动态更改对象的布局,因此不支持全局作用域和globalThis

TypeScript

// 全局文件中
var abc = 100

// 从上面引用'abc'
globalThis.abc = 200

ArkTS

// file1
export let abc : number = 0

// file2
import \* as M from "file1"

M.abc = 200

相关约束

  • 不支持声明函数的属性
  • 标准库使用限制
不支持一些utility类型

规则:arkts-no-utility-types

级别:错误

ArkTS仅支持PartialRecord,不支持TypeScript中其他的Utility Types

对于_Record_类型,表达式_rec[index]的类型是_V | undefined
对于Record类型,键-值中的值的类型必须是可选类型或者包含undefined的联合类型。

TypeScript

type Person = {
    name: string
    age: number
    location: string
}

type QuantumPerson = Omit

let persons : Record = {
    "Alice": {
        name: "Alice",
        age: 32,
        location: "Shanghai"
    },
    "Bob": {
        name: "Bob",
        age: 48,
        location: "New York"
    }
}
console.log(persons\["Bob"\].age)
console.log(persons\["Rob"\].age) // 运行时异常

ArkTS

class Person {
    name: string = ""
    age: number = 0
    location: string = ""
}

class QuantumPerson {
    name: string = ""
    age: number = 0
}

type OptionalPerson = Person | undefined
let persons : Record = {
// 或者
// let persons : Record = {
    "Alice": {
        name: "Alice",
        age: 32,
        location: "Shanghai"
    },
    "Bob": {
        name: "Bob",
        age: 48,
        location: "New York"
    }
}
console.log(persons\["Bob"\]!.age)
if (persons\["Rob"\]) { // 值检查,不会运行时抛异常
    console.log(persons\["Rob"\].age)
}
不支持对函数声明属性

规则:arkts-no-func-props

级别:错误

由于ArkTS不支持动态改变函数对象布局,因此,不支持对函数声明属性。

TypeScript

class MyImage {
    // ...
}

function readImage( path: string, callback: (err: any, image: MyImage) => void )
{
    // ...
}

function readFileSync(path : string) : number\[\] {
    return \[\]
}

function decodeImageSync(contrents : number\[\]) {
    // ...
}

readImage.sync = (path: string) => {
    const contents = readFileSync(path)
    return decodeImageSync(contents)
}

ArkTS

class MyImage {
    // ...
}

async function readImage( path: string, callback: (err: Error, image: MyImage) => void ) : Promise
{
    return await new MyImage()
}

function readImageSync(path: string) : MyImage {
    return new MyImage()
}

相关约束

  • 不支持globalThis
不支持Function.applyFunction.bind以及Function.call

规则:arkts-no-func-apply-bind-call

级别:错误

ArkTS不允许使用标准库函数Function.applyFunction.bind以及Function.call。标准库使用这些函数来显式设置被调用函数的this参数。在ArkTS中,this的语义仅限于传统的OOP风格,函数体中禁止使用this

TypeScript

class P1 {
  firstName: string = "Mary";
}

class P2 {
  firstName: string = "Jack";
  fullName(): string {
    return this.firstName
  }
}

const person2 = new P2();

const person1 = new P1();
// 将会打印 Mary
console.log(person2.fullName.apply(person1))

ArkTS

class Person {
    firstName : string

    constructor(firstName : string) {
        this.firstName = firstName
    }
    fullName() : string {
        return this.firstName
    }
}

let person = new Person("")
let person1 = new Person("Mary")

// 将打印“Mary”
console.log(person1.fullName())

相关约束

  • 不支持在函数中使用this
不支持readonly修饰函数参数

规则:arkts-no-readonly-params

级别:错误

当前ArkTS中,readonly可以用于修饰属性,但不能用于修饰函数参数。

TypeScript

function foo(arr: readonly string\[\]) {
    arr.slice()        // OK
    arr.push("hello!") // 编译时错误
}

ArkTS

function foo(arr: string\[\]) {
    arr.slice()        // OK
    arr.push("hello!") // OK
}
不支持as const断言

规则:arkts-no-as-const

级别:错误

ArkTS不支持as const断言。在标准TypeScript中,as const用于标注字面量的相应字面量类型,而ArkTS不支持字面量类型。

TypeScript

// 'hello'类型
let x = "hello" as const

// 'readonly \[10, 20\]'类型
let y = \[10, 20\] as const

// '{ readonly text: "hello" }'类型
let z = { text: "hello" } as const

ArkTS

// 'string'类型
let x : string = "hello"

// 'number\[\]'类型
let y : number\[\] = \[10, 20\]

class Label {
    text : string = ""
}

// 'Label'类型
let z : Label = {
    text: "hello"
}
不支持导入断言

规则: arkts-no-import-assertions

级别:错误

由于在ArkTS中,导入是编译时而非运行时特性,因此,ArkTS不支持导入断言。在运行时检查导入的API是否正确,对于静态类型的语言来说是没有意义的。改用常规的import语法。

TypeScript

import { obj } from "something.json" assert { type: "json" }

ArkTS

// 编译时将检查导入T的正确性
import { something } from "module"

相关约束

  • 不支持在模块名中使用通配符
  • 不支持通用模块定义(UMD)
  • 不支持运行时导入断言
限制使用标准库

规则:arkts-limited-stdlib

级别:错误

ArkTS不允许使用TypeScript或JavaScript标准库中的某些接口。大部分接口与动态特性有关。ArkTS中禁止使用以下接口:

全局对象的属性和方法:eval
InfinityNaNisFiniteisNaNparseFloatparseInt
EncodeDecodeParseHexOctet

Object__proto____defineGetter____defineSetter__
__lookupGetter____lookupSetter__assigncreate
definePropertiesdefinePropertyentriesfreeze
fromEntriesgetOwnPropertyDescriptorgetOwnPropertyDescriptors
getOwnPropertyNamesgetOwnPropertySymbolsgetPrototypeOf
hasOwnhasOwnPropertyisisExtensibleisFrozen
isPrototypeOfisSealedkeyspreventExtensions
propertyIsEnumerablesealsetPrototypeOfvalues

ReflectapplyconstructdefinePropertydeleteProperty
getgetOwnPropertyDescriptorgetPrototypeOfhas
isExtensibleownKeyspreventExtensionsset,
setPrototypeOf

Proxyhandler.apply()handler.construct()
handler.defineProperty()handler.deleteProperty()handler.get()
handler.getOwnPropertyDescriptor()handler.getPrototypeOf()
handler.has()handler.isExtensible()handler.ownKeys()
handler.preventExtensions()handler.set()handler.setPrototypeOf()

ArrayisArray

ArrayBufferisView

相关约束

  • 对象的属性名必须是合法的标识符
  • 不支持Symbol() API
  • 不支持通过索引访问字段
  • 仅允许在表达式中使用typeof运算符
  • 不支持in运算符
  • 禁止运行时检查对象属性
  • 不支持globalThis
强制开启严格类型检查

规则:arkts-strict-typing

级别:错误

在ArkTS中,以下TypeScript的严格类型检查选项必须打开:noImplicitReturns,
strictFunctionTypes, strictNullChecks, strictPropertyInitialization

TypeScript

class C {
    n: number // 只有在开启strictPropertyInitialization选项时会产生编译时错误
    s: string // 只有在开启strictPropertyInitialization选项时会产生编译时错误
}

// 只有在开启noImplicitReturns选项时会产生编译时错误
function foo(s: string): string {
    if (s != "") {
        console.log(s)
        return s
    } else {
        console.log(s)
    }
}

let n: number = null // 只有在开启strictNullChecks选项时会产生编译时错误

ArkTS

class C {
    n: number = 0
    s: string = ""
}

function foo(s: string): string {
    console.log(s)
    return s
}

let n1: number | null = null
let n2: number = 0

相关约束

  • 使用具体的类型而非any或unknown
  • 不允许通过注释关闭类型检查
不允许通过注释关闭类型检查

规则:arkts-strict-typing-required

级别:错误

在ArkTS中,类型检查不是可选项。不允许通过注释关闭类型检查,不支持使用@ts-ignore@ts-nocheck

TypeScript

// @ts-nocheck
// ...
// 关闭了类型检查后的代码
// ...

let s1: string = null // 没有报错

// @ts-ignore
let s2: string = null // 没有报错

ArkTS

let s1: string | null = null // 没有报错,合适的类型
let s2: string = null // 编译时报错

相关约束

  • 使用具体的类型而非any或unknown
  • 强制开启严格类型检查
允许ArkTS代码导入TS代码, 不允许TS代码导入ArkTS代码

规则:arkts-no-ts-deps

级别:错误

ArkTS中的代码可以导入来自标准TypeScript的代码,而标准TypeScript的代码不能导入来自ArkTS中的代码。

TypeScript

// app.ets
export class C {
    // ...
}

// lib.ts
import { C } from "app"

ArkTS

// lib1.ets
export class C {
    // ...
}

// lib2.ets
import { C } from "lib1"
除了ArkUI中的装饰器,不允许使用其他装饰器

规则:arkts-no-decorators-except-arkui

级别:错误

现在,ArkTS中只支持ArkUI中的装饰器。使用其他装饰器会造成编译时错误。

TypeScript

function classDecorator(x: number, y: number): void {
    //
}

@classDecorator
class BugReport {
}

ArkTS

function classDecorator(x: number, y: number): void {
    //
}

@classDecorator // 编译时错误
class BugReport {
}
class不能被用作对象

规则:arkts-no-classes-as-obj

级别:错误

在ArkTS中,class声明的是一个新的类型,不是一个值。因此,不支持将class用作对象(例如将class赋值给一个对象)。

TypeScript

class C {
    s: string = ""
    n: number = 0
}

let c = C
不支持在import语句前使用其他语句

规则:arkts-no-misplaced-imports

级别:错误

在ArkTS中,除动态import语句外,所有import语句需要放在所有其他语句之前。

TypeScript

class C {
    s: string = ""
    n: number = 0
}

import foo from "module1"

ArkTS

import foo from "module1"

class C {
    s: string = ""
    n: number = 0
}

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

你可能感兴趣的:(HarmonyOS,OpenHarmony,移动开发,typescript,ubuntu,linux,移动开发,harmonyos,openharmony)