HarmonyOS Next鸿蒙开发:TypeScript语法

186 阅读14分钟

如果你不是做前端的,那么学鸿蒙之前先了解ts,或者说先复习,学会ts就是学会鸿蒙arkts。

[toc]

2.2 TypeScript 快速⼊⻔

TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准 TypeScript 由微软开发的自由和开源的编程语言。 TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。

语言特性

TypeScript 是一种给 JavaScript 添加特性的语言扩展。增加的功能包括:

  • 类型批注和编译时类型检查
  • 类型推断
  • 类型擦除
  • 接口
  • 枚举
  • Mixin
  • 泛型编程
  • 名字空间
  • 元组
  • Await 以下功能是从 ECMA 2015 反向移植而来:
  • 模块
  • lambda 函数的箭头语法
  • 可选参数以及默认参数

JavaScript 与 TypeScript 的区别 TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。 TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

image.png

学习网址:www.runoob.com/typescript/…

2.2.1. 运⾏环境说明

2.2.1.1. 线上Playgroun

TypeScript提供了⼀个线上的 Playground 供练习使⽤,地址为 www.typescriptlang.org/zh/play

image.png

2.2.1.2. 本地运⾏环境

除去线上的运⾏环境,我们也可以在本地搭建⼀个 TS 的运⾏环境。 1.安装 VSCode 编辑器 VSCode是⼀款轻量级、开源且功能丰富的集成开发环境(IDE),⽀持多种编程语⾔,具有 强⼤的插件系统。下载地址为:code.visualstudio.com

image.png

2.安装Code Runner 插件 Code Runner是⼀款在VSCode中使⽤的插件,它提供了简便的代码执⾏功能,⽀持多种编 程语⾔,使开发者能够快速运⾏和调试代码⽚段。

image.png

3.安装ts-node

ts-node是⼀个 TypeScript 的运⾏环境,它允许我们直接运⾏ TypeScript 代码。ts-node 的安装和运⾏依赖于Node.js环境,因此在安装ts-node之前,我们需要准备好Node.js环 境。 Node.js是一个调用内置ApI并且基于Chrome V8引擎的js运行环境,之前自己在本地总结了一些零散的只知识点,今天整合一下发出来。

准备Node.js环境需要完成以下两步操作 安装Node.js nodejs.org/zh-cn/

配置环境变量 然后打开环境变量配置⾯板,按下 Win + R ,唤起运⾏窗⼝,之后运⾏命 令 sysdm.cpl

image.png

之后点击⾼级选项卡,并点击环境变量

image.png

然后在系统变量中选中 Path ,并点击编辑

image.png

之后点击新建,并填⼊Node.js的安装⽬录,完成后点击确定。

image.png 在配置完Node.js环境后,便可在终端执⾏以下命令来安装ts-node了

 npm install -g ts-node

注:完成后需要重新启动VSCode,另其重新加载环境变量和相关依赖。 4.编写程序并运 在完成上述环境的准备后,就可以编写Typescript程序并运⾏了,具体操作如下 ⾸先在合适的位置创建⼀个⼯程⽬录,例如 D:\workspace\hello-ts ,然后使⽤ VSCode打开⽬录

image.png

之后创建Typescript⽂件,点击New File

image.png

注意,⽂件的后缀为 .ts

image.png

之后就可以编写Typescript代码并运⾏了

image.png

2.2.2. 声明

2.2.2.1. 变量声明

image.png

2.2.2.2. 常量声明

let ⽤于声明变量,⽽const ⽤于声明常量,两者的区别是变量在赋值后可以修改,⽽常量在赋值后便不能再修改。

const b:number = 300;
2.2.2.3. 类型型推
2.2.3.1. number

number 表示数字,包括整数和浮点数,例如: 101 、-96 、 3.14 、-3.15

 let a :number = 101 
 let b :number = -96 
 let c :number = 3.14 
 let d :number = -3.15 
2.2.3.2. string

string 表示字符串,例如:你好,小老弟。 、 Hello, little buddy.

let a:string = ' '你好,小老弟。 ' 
let b:string = 'Hello, little buddy.'
2.2.3.3. boolean

boolean 表示布尔值,可选值为: true 、 false

let isOpen:boolean = true
let isDone:boolean = false
2.2.3.4. 数组

数组类型定义由两部分组成, 元素类型 [] ,例如 number[] 表示数字数组, 示字符串数组,数组类型的变量可由数组字⾯量—— string[] 表 [item1,item2,item3] 进⾏初始化。

let a: number[] = []
let b: string[] = ['你好,小冰老师!', 'Hello, Miss Ice']
2.2.3.5. 对象

在TS中,对象(object)是⼀种⼀系列由属性名称和属性值组成的数据结构,例如 姓名 :'张三', 年龄 :20, 性别 :' 男 ' 。对象类型的声明需要包含所有属性的名称及类型,例如 {name: string, age: number, gender: string} ,对象类型的变量可以通过对象字⾯量 —— {name:' 张三 ', age:20, gender:' 男 '} 进⾏初始化

let person: {name:string, age:number, gender:string} = {name:'张三', age:10, gender:'男'};

2.2.4. 函数

2.2.4.1. 函数声明语

声明函数的基础语法如下

image.png

2.2.4.2. 参数详解
2.2.4.2.1. 特殊语法
  • 可选参数 可选参数通过参数名后的 ? 进⾏标识,如以下案例中的 gender? 参数
function getPersonInfo(name: string, age: number, gender?: string): string {
	if (gender === undefined) {
 	gender = '未知'
 	}
	 return `name:${name},age:${age},gender:${gender}`;
 }

 let p1 = getPersonInfo('zhagnsan', 10, '男')
 let p2 = getPersonInfo('lisi', 15);
 console.log(p1);
 console.log(p2)

注:调⽤函数时,未传递可选参数,则该参数的值为 undefined 。

  • 默认参数 可在函数的参数列表为参数指定默认值,如以下案例中的 数。 gender: string=' 未知 '
function getPersonInfo(name: string, age: number, gender: string='未知'): 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)
2.2.4.3. 返回值详解
2.2.4.3.1. 特殊类型

若函数没有返回值,则可以使⽤ void 作为返回值类型,其含义为空。

function test(): void {
 console.log('hello');
}
2.2.4.3.2. 类型推断

函数的返回值类型可根据函数内容推断出来,因此可以省略不写。

function test() {
 console.log('hello');
}

function sum(a: number, b: number) {
 return a + b;
}
2.2.4.4. 函数声明特殊语法
  • 匿名函数 匿名函数的语法结构简洁,特别适⽤于简单且仅需⼀次性使⽤的场景。
let numbers: number[] = [1, 2, 3, 4, 5]
 numbers.forEach(function (number) {
 console.log(number);
 })

注意:匿名函数能够根据上下⽂推断出参数类型,因此参数类型可以省略。

  • 箭头函数 匿名函数的语法还可以进⼀步的简化,只保留参数列表和函数体两个核⼼部分,两者⽤ > 符号连接。
let numbers: number[] = [1, 2, 3, 4, 5]
 numbers.forEach((num) => { console.log(num) })

2.2.5. 类(class)

2.2.5.1. 概述

类(class)是⾯向对象编程语⾔中的⼀个重要概念。

⾯向对象编程(Object-Oriented Programming,简称OOP)是⼀种编程范式,其核⼼理念在于 将程序中的数据与操作数据的⽅法有机地组织成对象,从⽽使程序结构更加模块化和易于理解。 通过对象之间的协同合作,实现更为复杂的程序功能。

类(class)是对象的蓝图或模板,它定义了对象的属性(数据)和⾏为(⽅法)。通过类可以创 建多个具有相似结构和⾏为的对象。例如定义⼀个Student/Person类,其对象可以是张三同学、李四同学等等

2.2.5.2. 语法说明
2.2.5.2.1. 类的定义

定义类的语法如下图所示

image.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`
 	}
}
2.2.5.2.2. 对象创建
  • 语法

创建对象的关键字为 new ,具体语法如下

let person = new Person(1,'zhangsan')
  • 对象属性的访问
console.log(person.name); //读

person.name = 'lisi'; //写

console.log(person.name);
  • 对象⽅法的调⽤ 对象创建后,便可通过对象调⽤类中声明的⽅法,如下
let intro = person.introduce();
console.log(intro);
2.2.5.2.3. 静态成员

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'));`
2.2.5.3. 继承

继承是⾯向对象编程中的重要机制,允许⼀个类(⼦类或派⽣类)继承另⼀个类(⽗类或基类) 的属性和⽅法。⼦类可以直接使⽤⽗类的特性,并根据需要添加新的特性或覆盖现有的特性。这 种机制赋予⾯向对象程序良好的扩展性。 下⾯通过⼀个例⼦演示继承的特性

class Student extends Person {
 	classNumber: string;
	 constructor(id: number, name: string, classNumber: string) {
	 super(id, name);
 	 this.classNumber = classNumber;
	 }

 	introduce(): string {
 	   return super.introduce()+`, and I am a student`;
 	}
}


 let student = new Student(1,'xiaokeai','六年二班');
 console.log(student.introduce());

注意:

  • 类的继承需要使⽤关键字

  • extends ⼦类构造器中需使⽤

  • super() 调⽤⽗类构造器对继承⾃⽗类的属性进⾏初始化。

  • 在⼦类中可以使⽤ this 关键字访问继承⾃⽗类的属性和⽅法。 在⼦类中可以使⽤ super 关键字访问⽗类定义的⽅法。

2.2.5.4. 访问修饰符

访问修饰符(Access Modifiers)⽤于控制类成员(属性、⽅法等)的可访问性。TypeScript提 供了三种访问修饰符,分别是private、protected和 。

 class Person {
 	private id: number;
 	protected name: string;
 	public age: number;
 
     constructor(id: number, name: string, age: number) {
 		this.id = id;
 		this.name = name;
		this.age = age;
 	}	
 }

 class Student extends Person {
 
 }

说明:

  • private 修饰的属性或⽅法是私有的,只能在声明它的类中的被访问。

  • protected 修饰的属性或⽅法是受保护的,只能在声明它的类和其⼦类中被访问。同一个包内的其他类

  • public 修饰的属性或⽅法是公有的,可以在任何地⽅被访问到,默认所有的属性和⽅法都是 public 的

2.2.6. 接⼝(interface)

2.2.6.1. 概述

接⼝(interface)是⾯向对象编程中的另⼀个重要概念。接⼝通常会作为⼀种契约或规范让类 (class)去遵守,确保类实现某些特定的⾏为或功能

2.2.6.2. 语法说明
  • 接⼝定义 接⼝使⽤ interface 关键字定义,通常情况下,接⼝中只会包含属性和⽅法的声明,⽽不 包含具体的实现细节,具体的细节由其实现类完成。
interface Person {
 	id: number;
	name: string;
 	age: number;
	introduce(): void;
 }
  • 接⼝实现 接⼝的实现需要⽤到 及接⼝⽅法的实现逻辑。 implements 关键字,实现类中,需要包含接⼝属性的赋值逻辑,以及接⼝⽅法的实现逻辑。
class Student implements Person {
 	id: number;
	name: string;
 	age: number;
	
    constructor(id: number, name: string, age: number) {
 		this.id = id;
	    this.name = name;
		this.age = age;
	 }
 
 	introduce(): void {
 		console.log('Hello,I am a student');
    }
 }
2.2.6.3. 多态

多态是⾯相对象编程中的⼀个重要概念,它可以使同⼀类型的对象具有不同的⾏为。下⾯我们通 过⼀个具体的案例来体会多态这⼀概念 。 ⾸先,再创建⼀个 Person 接⼝的实现类 Teacher ,如下

class Teacher implements Person {
 		id: number;
 		name: string;
		age: number;
    
   
 		constructor(id: number, name: string, age: number) {
 			this.id = id;
			this.name = name;
			this.age = age;
		 }
    
 		introduce(): void {
			console.log('Hello,I am a teacher');
		}
 }

然后分别创建⼀个 Student 对象和⼀个 Teacher 对象,注意两个对象的类型均可以设置 Person ,如下

let p1: Person = new Student(1, 'xiaowang', 22);
let p2: Person = new Teacher(2, 'xiaozhu', 35);

最后分别调⽤p1 和 p2,调⽤同⼀个 introduce() ⽅法,你会发现,同样是 Person 类型的两个对 introduce() ⽅法时,表现出了不同的⾏为,这就是多态。

p1.introduce();//Hello,I am a student
p2.introduce();//Hello,I am a teacher
2.2.6.4. 接⼝的作⽤

在传统的⾯向对象编程的场景中,接⼝主要⽤于设计和组织代码,使代码更加容易扩展和维护。 下⾯举例说明。

假如现在需要实现⼀个订单⽀付系统,按照⾯向对象编程的习惯,⾸先需要定义⼀个订单类 (Order),如下

class Order {
 	totalAmount: number;
 
    constructor(totalAmount: number) {
		 this.totalAmount = totalAmount;
	}

 	pay() {
 		console.log(`AliPay:${this.totalAmount}`);
     }
    
}

很容易预想到,这个系统将来可能需要⽀持其他的⽀付⽅式,为了⽅便代码⽀持新的⽀付⽅式, 我们可以对代码进⾏如下改造。

⾸先定义⼀个⽀付策略的接⼝,接⼝中声明⼀个 pay ⽅法,⽤来规范实现类必须实现⽀付逻 辑

interface PaymentStrategy {
pay(amount: number): void;
}

然后在订单类中增加⼀个 PaymentStrategy 的属性,并且在订单类中的 pay ⽅法中调⽤ PaymentStrategy 的 pay ⽅法,如下

class Order {
 	totalAmount: number;
 	paymentStrategy: PaymentStrategy;
    
    
 	constructor(totalAmount: number, paymentStrategy: PaymentStrategy) {
 		this.totalAmount = totalAmount;
 		this.paymentStrategy = paymentStrategy;
	}
 
     pay() {
		 this.paymentStrategy.pay(this.totalAmount);
	 }
}

这样⼀来,之后创建的订单就可以使⽤ AliPay 这个⽀付⽅式了。

//支付案例
class Order {
    totalAmount: number;
    paymentStrategy?: PaymentStrategy

    constructor(totalAmount: number, paymentStrategy?: PaymentStrategy) {
        this.totalAmount = totalAmount;
        this.paymentStrategy = paymentStrategy
    }
    pay() {
        if (this.paymentStrategy === undefined) {
            console.log(`默认Pay:${this.totalAmount}`);
        } else {
            this.paymentStrategy.pay(this.totalAmount)
        }


    }
}
//其它支付方法
interface PaymentStrategy {
    pay(amount: number): void;
}

class Alipay implements PaymentStrategy {
    pay(amount: number) {
        console.log(`我是AliPay:${amount}}`);
    }
}

class WeiXinpay implements PaymentStrategy {
    pay(amount: number) {
        console.log(`我是WeiXinPay:${amount}}`);
    }
}

let order1 = new Order(2000)
let order2 = new Order(2000, new Alipay())
let order3 = new Order(2000, new WeiXinpay)
order1.pay()
order2.pay()
order3.pay()

2.2.6.5. TS 中的接⼝的特殊性

TypeScript 中的接⼝是⼀个⾮常灵活的概念,除了⽤作类的规范之外,也常⽤于直接描述对象的 类型,例如,现有⼀个变量的定义如下

let person: {name:string, age:number, gender:string} = 
    {name:'xiaowang', age:20, gender:'男'}

可以看到变量的值为⼀个⼀般对象,变量的类型为 {name:string, age:number, gender: string} ,此时就可以声明⼀个接⼝来描述该对象的类型,如下

interface Person {
	 name: string;
	 age: number;
	 gender: string;
 }
 
let person: Person = {name:'张三', age:10, gender:'男'};

2.2.7. 枚举

2.2.7.1. 概述

枚举(Enumeration)是编程语⾔中常⻅的⼀种数据类型,其主要功能是定义⼀组有限的选项, 例如,⽅向(上、下、左、右)或季节(春、夏、秋、冬)等概念都可以使⽤枚举类型定义。

2.2.7.2. 语法说明
  • 枚举定义

枚举的定义需使⽤ enum 关键字,如下

enum Season {
 	SPRING,
	SUMMER,
	AUTUMN,
 	WINTER
 }
  • 枚举使⽤ 枚举的使⽤记住两个原则即可 枚举值的访问 像访问对象属性⼀样访问枚举值,例如 **枚举值的类型 ** 枚举值的类型为 enum 的名称,例如 Season.SPRING 和 Season.SUMMER类型都是 Season
let spring:Season = Season.SPRING;
  • 使⽤场景 现需要编写⼀个函数 move ,其功能是根据输⼊的⽅向(上、下、左、右)进⾏移动,此时 就可以先使⽤枚举定义好所有可能的输⼊选项,如下
enum Direction {
 	UP,
	BOTTOM,
	LEFT,
	RIGHT
}

move 函数的实现如下

function move(direction: Direction) {
 if(direction===Direction.UP){
 		console.log('向上移动');
 }else if(direction===Direction.BOTTOM){
		 console.log('向下移动');
 }else if(direction===Direction.LEFT){
         console.log('向左移动');
 }else{
     	 console.log('向右移动');
 }
    
}


 move(Direction.UP);
2.2.7.3. 赋值

在TypeScript 中,枚举实际上是⼀个对象,⽽每个枚举值都是该对象的⼀个属性,并且每个属性 都有具体的值,属性值只⽀持两种类型—— 数字或字符串。

默认情况下,每个属性的值都是数字,并且从 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

通过为枚举属性赋值,可以赋予枚举属性⼀些更有意义的信息,例如以下枚举

enum Color {
 	Red = 0xFF0000,
 	Green = 0x00FF00,
 	Blue = 0x0000FF
 }
 
enum FontSize {
	 Small = 12,
 	 Medium = 16,
	 Large = 20,
	 ExtraLarge = 24
 }

2.2.8. 模块化

2.2.8.1. 概述

模块化是指将复杂的程序拆解为多个独⽴的⽂件单元,每个⽂件被称为⼀个模块。

在 TypeScript 中,默认情况下,每个模块都拥有⾃⼰的作⽤域,这意味着在⼀个模块中声明的任何 内容(如变量、函数、类等)在该模块外部是不可⻅的。

为了在⼀个模块中使⽤其他模块的内 容,必须对这些内容进⾏导⼊、导出。

转存失败,建议直接上传图片文件

2.2.8.2. 语法说明
  • 导出 导出须使⽤ export 关键字,语法如下 moduleOne.ts
 export function hello() {
 console.log('hello module One');
 }
​
 export const str = 'hello world';
​
 const num = 1;
  • 导⼊

导⼊须使⽤ import 关键字,语法如下

moduleTwo.ts

 import { hello, str } from './moduleOne';

 hello();//从1号模块导过来
 console.log(str);//导过来的字符
2.2.8.3. 避免命名冲突

若多个模块中具有命名相同的变量、函数等内容,将这些内容导⼊到同⼀模块下就会出现命名冲 突。例如,在上述案例的基础上,⼜增加了⼀个 moduleC,内容如下

 export function hello() {
 console.log('hello module C');
 }

 export const str = 'module C';

moduleTwo 同时引⼊ moduleOne 和 moduleC 的内容,如下,显然就会出命名冲突

import { hello, str } from "./moduleOne";
import { hello, str } from "./moduleC";

 hello() //?
 console.log(str); //?

有多种⽅式可以⽤来解决命名冲突,下⾯逐⼀介绍

  • 导⼊重命名 语法如下
 import { hello as helloFromOne, str as strFromOne } from "./moduleOne";
 import { hello as helloFromC, str as strFromC } from "./moduleC";

 helloFromOne();
 console.log(strFromOne);

 helloFromC();
 console.log(strFromC)
  • 创建模块对象 上述导⼊重命名的⽅式能够很好的解决命名冲突的问题,但是当冲突内容较多时,这种写法 会⽐较冗⻓。除了导⼊重命名外,还可以将某个模块的内容统⼀导⼊到⼀个模块对象上,这样就能简洁有效的解决命名冲突的问题了,具体语法如下
 import * as One from "./moduleOne";
 import * as C from "./moduleC";
 One.hello();
 console.log(One.str);
 C.hello();
 console.log(C.str);
2.2.8.4. 默认导⼊导出

除了上述导⼊导出的语法之外,还有⼀种语法,叫做默认导⼊导出,这种语法相对简洁⼀些。

  • 默认导出

默认导出允许⼀个模块指定⼀个(最多⼀个)默认的导出项,语法如下

moduleOne.ts

 export default function hello(){
 console.log('moduleOne');
 }
  • 默认导⼊ 由于每个模块最多有⼀个默认导出,因此默认导⼊⽆需关注导⼊项的原始名称,并且⽆需使 ⽤ {} 。

moduleB.ts

 import helloFromOne from "./moduleOne";

由于默认导⼊时⽆需关注导⼊项的名称,所以默认导出⽀持匿名内容,⽐如匿名函数,语法 如下

moduleOne.ts

export default function () {
 	console.log('moduleB');
 }
 

常见类型转换

let string1:string ="1.7"
let string2:string ="1.7a"
let string3:string ="1.7"

console.log("string1",parseInt(string1))//1
console.log("string2",parseFloat(string2))//1.7
console.log("string3",parseFloat(string3))//1.7
console.log("string3",Number(string3))//1.7
console.log("string3",Number(string2))//Nan

let number1:number=1.1
let number2:number=1.9

console.log("number1",number1.toString())//1.1
console.log("number2",number1.toFixed())//1
console.log("number3",number2.toFixed())//2
console.log("number4",number2.toFixed(2))//1.90
  • 作者:黄东华
  • 联系方式:18128822181
  • 本手册部分内容来源于互联网。如有侵权或疑问,请及时联系作者,我将立即处理并进行删除相关内容。