十念 发表于 2024-11-15 08:41:47

鸿蒙-TypeScript语法

1. 概述


https://img-blog.csdnimg.cn/img_convert/e4a577f32e8d2ccf698519070ac45d03.png
HarmonyOS 应用的主要开发语言是 ArkTS,它由 TypeScript(简称TS)扩展而来,在继承TypeScript语法的底子上举行了一系列优化,使开发者可以或许以更简便、更自然的方式开发应用。
留意:TypeScript 自己也是由另一门语言 JavaScript 扩展而来,它主要是在JavaScript的底子上添加了静态类型定义。因此三者的关系如下图所示

2. TypeScript 快速入门

TypeScript提供了一个线上的 Playground 供训练使用,地址为TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript。

https://img-blog.csdnimg.cn/img_convert/0404e15764c5e3ff0e773f587bc25ac5.png

2.1. 声明

变量声明
https://img-blog.csdnimg.cn/img_convert/9b5e13915b3521cec335c2cc04b129c8.png
常量声明
let用于声明变量,而const用于声明常量,两者的区别是:变量在赋值后可以修改,而常量在赋值后便不能再修改。
let a:number = 100
const b:number = 200; 类型推断
如果一个变量或常量的声明包罗了初始值,TS 便可以根据初始值举行类型推断,此时我们就可以不消显式的指定其类型,比方:
let c = 60;
console.log(typeof c); //number 2.2. 常用数据类型

number类型
number表现数字,包括整数和浮点数,比方: 340、-23 、29.5、-13.4
let a :number = 340
let b :number = -23
let c :number = 29.5
let d :number = -13.4 string类型
string表现字符串,比方: 你好、hello
let a:string = '你好'
let b:string = 'hello' boolean类型
boolean表现布尔值,可选值为:true、false
let isOpen:boolean = true
let isDone:boolean = false 数组类型
数组类型定义由两部门组成,元素类型[],比方number[]表现数字数组,string[]表现字符串数组,数组类型的变量可由数组字面量——举行初始化。
let a: number[] = []
let b: string[] = ['你好', 'hello'] 对象类型
对象(object)类型的声明很简朴,只需声明属性名称和属性类型即可,比方{id:number,name:string},对象类型的变量可以通过对象字面量——{id:1,name:'zhangsan'}举行初始化。
let person: { id: number, name: string } = { id: 1, name: 'zhangsan' };
2.3. 函数


https://img-blog.csdnimg.cn/img_convert/3f5c6fae4e6f18f0ccdc3a96003f7018.png
可选参数:可选参数通过参数名后的?举行标识,比方下面的gender?参数。
function getPersonInfo(name: string, age: number, gender?: string) {
    if (!gender) {
      gender = '未知'
    }
    return `name:${name},age:${age},gender:${gender}`;
}

let p1 = getPersonInfo('zhagnsan', 10, '男')
let p2 = getPersonInfo('lisi', 15);
console.log(p1);
console.log(p2); 默认参数:可在函数的参数列表为参数指定默认值,如以下案例中的gender: string='未知'参数。
function getPersonInfo(name: string, age: number, gender: string='未知') {
    return `name:${name},age:${age},gender:${gender}`;
}

let p1 = getPersonInfo('zhagnsan', 10, '男')
let p2 = getPersonInfo('lisi', 15);
console.log(p1);
console.log(p2); 联合类型:一个函数可能用于处理差别类型的值,这种环境可以使用联合类型,比方以下案例中的message: number | string
function printNumberOrString(message: number | string) {
console.log(message)
}

printNumberOrString('a')
printNumberOrString(1) 任意类型:若函数需要处理任意类型的值,则可以使用any类型,比方以下案例中的message: any
function print(message:any) {
console.log(message)
}

print('a')
print(1)
print(true) 特别类型:若函数没有返回值,则可以使用void作为返回值类型,其含义为空。
function test(): void {
    console.log('hello');
} 类型推断:函数的返回值类型可根据函数内容推断出来,因此可以省略不写。
function test() {
    console.log('hello');
}

function sum(a: number, b: number) {
    console.log(a + b);
} 匿名函数:匿名函数的语法布局简便,特别适用于简朴且仅需一次性使用的场景。
let numbers: number[] =
numbers.forEach(function (number) {
    console.log(number);
}) 留意:匿名函数可以或许根据上下文推断出参数类型,因此参数类型可以省略。

箭头函数:匿名函数的语法还可以进一步的简化,只保留参数列表和函数体两个焦点部门,两者用=>符号连接。
let numbers: number[] =
numbers.forEach((num) => { console.log(num) })
2.4. 类(class)

概述:
类(class)是对象的蓝图或模板,它定义了对象的属性(数据)和行为(方法)。通过类可以创建多个具有相似布局和行为的对象。比方定义一个 Person类,其对象可以有张三、李四等等。

通过一个简朴案例,学习一下类的定义语法

https://img-blog.csdnimg.cn/img_convert/d1a84ba71b7c620608928ccdbaee2769.png
示例代码:
class Person {
    id: number;
    name: string;
    age: number = 18;

    constructor(id: number, name: string) {
      this.id = id;
      this.name = name;
    }

    introduce(): string {
      return `hello,I am ${this.name},and I am ${this.age} years old.`
    }
}
对象创建:创建对象的关键字为new,具体语法如下
let person = new Person(1,'zhangsan,10); 对象属性的访问
console.log(person.name); //读

person.name = 'lisi'; //写

console.log(person.name); 对象方法的调用:对象创建后,便可通过对象调用类中声明的方法,如下
let intro = person.introduce();
console.log(intro);
静态成员
Typescript 中的类中可以包罗静态成员(静态属性和静态方法),静态成员从属于类自己,而不属于某个对象实例。静态成员通用用于定义一些常量,大概工具方法。



[*]声明静态成员:定义静态成员需要使用static关键字。
class Constants{
    static count:number=1;
}

class Utils{
    static toLowerCase(str:string){
      return str.toLowerCase();
    }
}

console.log(Constants.count);
console.log(Utils.toLowerCase('Hello World'));

[*]使用静态成员:静态成员无需通过对象实例访问,直接通过类自己访问即可。
console.log(Constants.count);
console.log(Utils.toLowerCase('Hello World')); 继承:
继承是面向对象编程中的重要机制,允许一个类(子类或派生类)继承另一个类(父类或基类)的属性和方法。子类可以直接使用父类的特性,并根据需要添加新的特性或覆盖现有的特性。这种机制赋予面向对象程序良好的扩展性。
下面通过一个例子演示继承的特性
class Student extends Person {
    classNumber: string;
    constructor(id: number, name: string, age: number, classNumber: string) {
      super(id, name, age);
      this.classNumber = classNumber;
    }

    introduce(): string {
      return `hello,I am ${this.name},and I am ${this.age} years old, and I am a student`
    }
}

let student = new Student(1,'xiaoming',10,'三年二班');
console.log(student.introduce());

[*]关键字:extends
[*]新增属性:classNumber:string;
[*]覆盖父类方法:introduce
[*]构造器:子类构造器中需调用super(父类构造器)对继承的父类属性举行初始化。

权限修饰符
权限修饰符用于控制类成员(属性、方法等)的访问权限。它们有助于维护代码的封装性和安全性。TypeScript提供了三种访问修饰符,分别是 public、private 和 protected。


[*]public :公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的。
[*]private:私有的,只能在声明它的类中的被访问。
[*]protected:受掩护的,只能在声明它的类和其子类中被访问。

2.5. 接口(interface)

概述
接口(interface)是面向对象编程中的另一个重要概念。接口通常会作为类(class)的一种契约或规范,确保类实现了特定的行为或功能。通常环境下,接口中只会包罗属性和方法的声明,而不包罗具体的实现细节,具体的细节由实在现类完成。

接口定义:接口使用interface关键字定义,具体如下
interface Person {
    id: number;
    name: string;
    age: number;
    job: string;

    introduce(): void;
} 接口实现
接口的实现需要用到implements关键字,实现类中,需要包罗接口属性的赋值逻辑,以及接口方法的实现逻辑。
class Teacher implements Person {
    id: number;
    name: string;
    age: number;
    job: string = 'Teacher';

    constructor(id: number, name: string, age: number) {
      this.id = id;
      this.name = name;
      this.age = age;
    }

    introduce(): void {
      console.log(`Hello,I am ${this.name}`);
    }
} 作用
在传统的面向对象编程的场景中,接口主要用于计划和构造代码,使代码更加容易扩展和维护。下面举例说明。假如如今需要实现一个订单支付体系,按照面向对象编程的习惯,起首需要定义一个订单类(Order),如下
class Order {
    total_amount: number;

    constructor(total_amount: number) {
      this.total_amount = total_amount;
    }

    pay() {
      console.log(`Pay:${this.total_amount}`);
    }
} 很容易预想到,订单需要未来可能需要支持多种支付方式,为了方便后期让代码支持新的支付方式,我们可以对代码举行如下改造。起首定义一个支付策略的接口,接口中声明了一个pay方法,用来规范实现类必须实现支付逻辑。
interface PaymentStrategy {
    pay(amount: number): void;
} 然后在订单类中增长一个PaymentStrategy的属性,而且在订单类中的pay方法中调用PaymentStrategy的pay方法,如下
class Order {
    total_amount: number;
    paymentStrategy: PaymentStrategy;

    constructor(total_amount: number, paymentStrategy: PaymentStrategy) {
      this.total_amount = total_amount;
      this.paymentStrategy = paymentStrategy;
    }

    pay() {
      this.paymentStrategy.pay(this.total_amount);
    }
} 如许改造完之后,就可以很容易的在不改变现有代码的环境下,支持新的支付方式了。
比如如今需要支持AliPay,那我们就可以创建AliPay这个类(class)并实现(implement)PaymentStrategy这个接口,如下
class AliPay implements PaymentStrategy {
    pay(amount: number): void {
      console.log(`AliPay:${amount}`);
    }
} 如许一来,之后创建的订单就可以使用AliPay这个支付方式了。
let order = new Order(1000,new AliPay());
order.pay();
TS 中的接口的特别性
TypeScript 中的接口是一个非常机动的概念,除了用作类的规范,让类去实现之外,也常用于直接形貌对象的类型,比方,现有一个方法的定义如下
function getPersonInfo(person: { id: number, name: string, age: number }) {
    console.log(``);
} 可以看到该函数的参数类型为一个一般对象:{ id: number, name: string, age: number },此时就可以声明一个接口来形貌参数类型,如下,如许一来,函数定义就会更加简便明了。
interface Person {
    id: number;
    name: string;
    age: number;
}

function getPersonInfo(person: Person) {
    console.log(``);
} 为使接口使用起来更加机动,TypeScript提出了可选属性的概念,语法如下
interface Person {
    id: number;
    name: string;
    age?: number;
} 上述接口中的age字段就是一个可选属性,在声明该接口类型的对象时,便可根据实际环境选择性的包罗大概不包罗 age字段,如下
getPersonInfo({ id: 1, name: 'zhangsan', age: 10 })
getPersonInfo({ id: 2, name: 'lisi' }) 2.6. 枚举

概述
枚举(Enumeration)是一种编程语言中常见的数据类型,用于定义一组有限的定名常量,常用于表现特定的状态、类型或选项,比方方向(上、下、左、右)、季候(春、夏、秋、冬)等。
enum Direction {
    UP,
    BOTTOM,
    LEFT,
    RIGHT
}

function move(direction: Direction) {
    switch (direction) {
      case Direction.UP:
            console.log('向上移动');
            break;
      case Direction.BOTTOM:
            console.log('向下移动');
            break;
      case Direction.LEFT:
            console.log('向左移动');
            break;
      case Direction.RIGHT:
            console.log('向右移动');
            break;
      default:
            console.log('原地不动')
            break;
    }
}

move(Direction.UP); 枚举的使用记着两个原则

[*]枚举值的访问
像访问对象属性一样访问枚举值,比方Direction.UP

[*]枚举值的类型
枚举值的类型为enum的名称,比方Direction.UP和Direction. BOTTOM等值的类型都是Direction

枚举原理
默认环境下,每个属性的值都是数字,而且从 0 开始递增,比方上述案例中的Direction枚举中,Direction.UP的值为0,Direction.BOTTOM的值为1,依次类推,具体如下
console.log(Direction.UP) //0
console.log(Direction.BOTTOM) //1
console.log(Direction.LEFT) //2
console.log(Direction.RIGHT) //3 除了使用默认的数字作为属性的值,我们还能手动为每个属性赋值,比方
enum Direction {
    UP = 1,
    BOTTOM = 2,
    LEFT = 3,
    RIGHT = 4
}

console.log(Direction.UP) //1
console.log(Direction.BOTTOM) //2
console.log(Direction.LEFT) //3
console.log(Direction.RIGHT) //4 再比方
enum Direction {
    UP = 'up',
    BOTTOM = 'bottom',
    LEFT = 'left',
    RIGHT = 'right'
}

console.log(Direction.UP) //up
console.log(Direction.BOTTOM) //bottom
console.log(Direction.LEFT) //left
console.log(Direction.RIGHT) //right 留意:
多数环境下,我们只是用枚举来表现几种差别的状态,以便在差别的状态下采取差别的举措,这时一般无需关注每个属性具体的值。
2.7. 模块化

概述
模块化是将复杂的程序拆解为多个独立的文件单位,每个文件被称为一个模块。在 TypeScript 中,默认环境下,每个模块都拥有自己的作用域,这意味着在一个模块中声明的任何内容(如变量、函数、类等)在该模块外部是不可见的,除非明确导出。同时,为了在一个模块中使用其他模块的内容,必须先将这些内容显式导入到当前模块中。

https://img-blog.csdnimg.cn/img_convert/79fb4c2b4e80f36574cdc474a667445b.png
导出:导出须使用export关键字,语法如下
export function hello() {
    console.log('hello module A');
}

export const str = 'hello world';

const num = 1; 导入:导入须使用import关键字,语法如下
import { hello, str } from './moduleA';

hello();
console.log(str); 避免定名冲突
若多个模块中具有定名雷同的变量、函数等内容,将这些内容导入到同一模块下就会出现定名冲突。比方,在上述案例的底子上,又增长了一个 moduleC,内容如下
export function hello() {
    console.log('hello module C');
}

export const str = 'module C'; moduleB 同时引入 moduleA 和 moduleC 的内容,如下,显然就会出定名冲突
import { hello, str } from "./moduleA";
import { hello, str } from "./moduleC";

hello() //?
console.log(str); //? 有多种方式可以用来办理定名冲突,下面逐一介绍


[*]导入同时举行重定名
import { hello as helloFromA, str as strFromA } from "./moduleA";
import { hello as helloFromC, str as strFromC } from "./moduleC";

helloFromA();
console.log(strFromA);

helloFromC();
console.log(strFromC);

[*]创建模块对象
上述导入重定名的方式可以或许很好的办理定名冲突的问题,但是当冲突内容较多时,这种写法会比较冗长。除了导入重定名外,还可以将某个模块的内容同一导入到一个模块对象上,如许就能简便有效的办理定名冲突的问题了,具体语法如下
import * as A from "./moduleA";
import * as C from "./moduleC";

A.hello();
console.log(A.str);

C.hello();
console.log(C.str);
除了上述导入导出的语法之外,还有一种语法,叫做默认导入导出,这种语法相对简便一些。


[*]默认导出:默认导出允许一个模块指定一个(最多一个)默认的导出项,语法如下
export default function hello(){
    console.log('moduleA');
} 默认导出支持匿名导出项,语法如下
export default function () {
    console.log('moduleB');
}

[*]默认导入
由于每个模块最多有一个默认导出,因此默认导入无需指定导入项的原名称,而且无需使用{}。
import helloFromA from "./moduleA";
import helloFromB from "./moduleB"; 上述语法相当于以下语法的简写
import { default as helloFromA } from "./moduleA";
import { default as helloFromB } from "./moduleB"; 2.8. 装饰器

概述
在TypeScript中,装饰器是一种特别类型的声明,可以被附加到类,属性,方法上。装饰器的焦点头脑是在尽量不改变原始类的定义的环境下,为类添加新的特性。
装饰器使用@Expression的形式。其中,Expression为一个函数,这个函数负责定义为类添加的新特性,其会在运行时被调用。
ArkTS 提供了多种装饰器,同时在鸿蒙应用的开发中我们也会大量使用这些装饰器,因此学习装饰器语法,对于明白鸿蒙应用的执行原理有很大资助。

装饰器的分类
按照声明位置的差别,装饰器可以分为类装饰器、方法装饰器和属性装饰器等,差别装饰器可用于实现差别的功能。


[*]类装饰器:
类装饰器可以拦截并修改类的构造函数,这使得我们可以在实例化对象时增长一些额外的逻辑。
@ClassDecorator
class A {

}

/**
* 装饰器函数
* 在第一次引用A类时执行
* @param target被装饰的类
*/
function ClassDecorator (target) {
// 给目标类添加静态属性
target.xxx = 'abc'
}

console.log((A as any).xxx)// abc


[*]方法装饰器
使用方法装饰器可以拦截并修改所装饰的方法,这使得我们可以在调用该方法之前或之后执行一些额外的逻辑。
class A {
@MethodDecorator
hello(){
    console.log('hello()');
}
}

/**
* 方法装饰器
* 在第一次调用方法前执行
* @param target 方法所属类的原型对象
* @param name 方法名
* @param descriptor 方法属性对应的描述符对象
*/
function MethodDecorator (target: object, name: string, descriptor: PropertyDescriptor) {
console.log(`${name}方法将要第一次调用了`, target, name, descriptor);
}

const a = new A()
a.hello()


[*]属性装饰器
属性装饰器可以拦截对属性的读写操纵,并在这些操纵之前或之后执行一些额外的逻辑。
class A {
@PropertyDecorator
name: string;
}

/**
* 属性装饰器
* 在第一次创建对象内部初始化属性时执行
* @param target 属性所属类的原型对象
* @param name 属性名
*/
function PropertyDecorator (target: object, name: string) {
console.log(`将要第一次操作${name}属性`, target, name);
}

const a = new A() // 内部会将name属性添加给a对象,自动调用PropertyDecorator
简朴案例
下面通过一个简朴的案例,演示装饰器的作用,比方如今需要监听某一个属性的变化,当其发生变化时,自动执行一些额外的逻辑。此时就可以通过一个属性装饰器来监视读写操纵。
class Person {
@log
name: string;

constructor(name: string) {
    this.name = name;
}
}

function log(target: object, name: string) {
console.log('----------log')
let value: any;
Object.defineProperty(target, name, {
    set (newValue: any) {
      console.log(`监视到${name}属性修改为${newValue}`);
      value = newValue;
    },
    get () {
      console.log(`监视到读取${name}属性`)
      return value;
    }
})
}

let person = new Person('张三');
person.name='李四'
console.log(person.name)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 鸿蒙-TypeScript语法