构建鸿蒙5.0应用-语法篇-TypeScript(三)

        ArkTs是OpenHarmony主推的应用开发语言,ArkTS围绕应用开发在TypeScript生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,当然这是官方的表述,真正想了解清楚两者的区别当然还是得认真学习一下TypeScript与ArkTS的,完整资料参考typescript超详细入门教程(上)

1、typescript常用基本数据类型:

类型说明允许的示例

number

数值类型

0、0.1、-1、Math.PI、300*10、Infinity、0x2e21

string

字符串类型‘’、‘张三’
boolean布尔类型true\false
null空类型null
undefined未定义类型 undefined
object对象类型,不接受null和undefined

{}、new Map()

any任意类型0、‘张三’、{}、undefined、null、true
enum枚举类型,若未定义值时默认从0开始/
void空类型,一般用于函数返回值的类型声明let a:() => void,表示a允许被赋值为一个函数,该函数不返回值
tuple        元组,支持接受多种类型的数组,例如[string,number]

['12',12],支持多样数据类型的组合

字面量把指定的值作为类型使用,限制该变量的取值范围

let type: 'dog'|'cat'|'rabbit' = 'cat',只能从'dog'|'cat'|'rabbit'中取值

2、enum枚举类型的使用:

// 定义枚举类型
enum Colors {
  Red = 'red',
  Green = 'green',
  Blue = 'blue'
}

console.log(Colors.Red) // 输出red

3、interface接口的使用:

接口是用于定义对象结构的类型,如果某个字段是对象中非必要的,可以在interface后面增加‘?’表示:

enum Colors {
  Red = 'red',
  Green = 'green',
  Blue = 'blue'
}
console.log(Colors.Red)
interface student {
  name: string // 字符串类型
  age: number // 数值类型
  bool: boolean // 布尔类型
  nullEl: null // Null类型
  udf: undefined // undefined类型
  obj: Object // 对象类型
  anyType: any // 任意类型
  color: Colors // 枚举类型
  result: void // 空类型
  numberArr: number[] // 数值类数组
  stringArr: string[] // 字符串数组
  syb: Symbol // Symbol类型
  nessaray?: boolean // 非必选的字段
  array: [string, boolean, number] // 元组类型
  animals: { name: 'cat' } | 'dog' // 字面量
  print?: () => void // 函数
}

const student1: student = {
  name: '张三',
  age: 0x2e21,
  bool: false,
  nullEl: null,
  udf: undefined,
  obj: true,
  anyType: null,
  color: Colors.Red,
  result: (() => console.log(123))(),
  numberArr: [1, 2, 3],
  stringArr: ['1', '2', '3'],
  syb: Symbol('syb'),
  print: () => console.log('打印'),
  array: ['', true, 23],
  animals: { name: 'cat' }
}
student1.print?.()

多余属性检查:

以下这种情况赋值student类型时,会提示多了age属性:

如果要允许多余属性赋值,可以通过类型断言绕开多余属性检查:

第二种方式是通过添加类型索引签名,其中‘key’可以是任意字符串,你定义为prop、a、b这些也无所谓:

 4、定义函数

定义函数有两个要点:

1)为函数入参声明类型

2)为函数声明返回类型

interface Cat {
  name:string;
}
interface Mouse {
  name:string;
}
const tom:Cat = {
  name: 'Tom'
}
const jerry:Mouse = {
  name: 'Jerry'
}
function tellStory (cat:Cat, mouse:Mouse):void {
  console.log(`${cat.name} catch ${mouse.name}`)
}
let tellStory2 = (cat:Cat, mouse:Mouse):void => {
  console.log(`${cat.name} catch ${mouse.name}`)
}
tellStory(tom, jerry)// 输出Tom catch Jerry
tellStory2(tom, jerry)// 输出Tom catch Jerry

5、交叉类型

交叉类型使用&符号连接两种类型,可以表示一个返回值具有这两种类型的属性,这里举个例子:警察可以抓贼,医生可以治疗病人,这两个人搭伙可以干什么?

interface Police {
  catchTheif:() => void;
}
interface Doctor {
  health:() => void;
}
const tom:Police = {
  catchTheif: () => {
    console.log('我可以抓贼')
  }
}
const jerry:Doctor = {
  health: () => {
    console.log('我可以治疗病人')
  }
}
function whatCanYouDo (person1:Police, person2:Doctor):Police & Doctor {
  return Object.assign(person1, person2)
}
let partner = whatCanYouDo(tom, jerry)

partner.catchTheif() // 我可以抓贼
partner.health() // 我可以治疗病人

交叉类型中类型A和类型B不能拥有相同的属性,以下情况会报错:

 6、联合类型:

如果某个方法适用于类型A和类型B或其他更多类型的参数传入后执行,可以使用联合类型,例如person就可以接受Police和Doctor类型的参数传入,因为他们都具有函数体所需要的属性和方法。

interface Police {
  name:string;
  run:() => void;
}
interface Doctor {
  name:string;
  run:() => void;
}
const tom:Police = {
  name:'tom',
  run: () => {
    console.log('我可以跑起来!')
  }
}
const jerry:Doctor = {
  name:'jerry',
  run: () => {
    console.log('我也可以跑起来!')
  }
}
function whatCanYouDo (person:Police|Doctor):void {
  console.log(`${person.name}:`)
  person.run()
}
whatCanYouDo(tom)// 输出tom:我可以跑起来!
whatCanYouDo(jerry) // 输出jerry:我也可以跑起来!

如果Doctor缺少name属性时,函数体会提示相关错误:

7、泛型

 泛型是指在定义接口、函数、或者类时,不预先定义参数类型,在使用时再指定参数类型的一种特性。

下面这种泛型函数就像是无情的出租车,只要是你给了钱,我就能把你送到目的地,不管乘客是人还是狗:

//<T>预定义泛型类型有哪些类型,T[]代表返回T类型的数组
function fill<T>(param:T):T[] {
  return new Array(3).fill(param)
}
console.log(fill(3))
console.log(fill({a: 1}))

但是不是所有的乘客都可以上车,要声明你有钱才行:

interface hasLength {
  length:number
}
function getLength<T extends hasLength>(param: T):number {
  return param.length
}
const data:hasLength = {
  length: 12
}
console.log(getLength('str344')) // 隐藏了参数类型
console.log(getLength<number[]>([12,12,12]))// 展示了参数类型
console.log(getLength(data))// 12
console.log(getLength(12))// 报错

定义泛型函数接口:

interface hasLength {
  length:number
}
interface GetLength {
  <T extends hasLength>(param:T):number;
}
let getLength:GetLength = (param) => {
  return param.length
}
// 泛型定义到外层,供内部使用
interface GetArray<T> {
  (arg: T):void;
  tag: T;
}
const arr:GetArray<number> = <T>(length: T):void => {
  console.log('I has length:', length)
}
arr.tag = 12

interface hasLength2 {
  length:number
}
interface hasName {
  name:string
}
function logger <T extends (hasLength2 | hasName)>(param:T, prop:string):void {
  if(param.hasOwnProperty(prop)) {
    console.log('param has property:', prop)
  }
}
let value = {name: '张三'}
logger([12],'length')
logger(value, 'we')

8、readonly的使用:

在接口中定义readonly属性,对该属性重新赋值时会提示错误。

定义只读数组:

9、继承

继承单个接口 

interface Person {
  name: string
}
interface Employee extends Person {
  title: string
}
let joe: Employee = {
  name: '张三',
  title: '前端工程师'
}
console.log(joe)

 继承多个接口:

interface Person {
  name: string
}
interface Job {
  duty: string
}
interface Employee extends Person, Job {
  title: string
}
let joe: Employee = {
  name: '张三',
  title: '前端工程师',
  duty: '写代码'
}
console.log(joe)

设计程序的时候有时候会困惑什么时候C extends A,B,什么时候C extends B, B extends A:

往往A和B是不同维度的东西的时候,C要extends A,B

A和B是同一维度的东西或具有从属关系的时候,C extends B, B extends A

比如一个社会心理学的学士(C),他继承了学生(B)的属性,学位是社会心理学,班级是xx年级XX班;同时他也是XX公司的员工,继承了职工(A)的属性,职责是社会调研,工资是3k,这种往往是C要extends A,B

比如狸花猫(C),它是猫科动物(B),同时也是动物(A),这种往往是C extends B, B extends A。

10、修饰符

在ECMAScript中定义类,static是ES标准支持的修饰符:

class Person1 {
	MAX_AGE = 150
	constructor() {
		console.log(this.MAX_AGE) // 通过实例化访问
	}
}
new Person1() // 150

class Person {
	static MAX_AGE = 150
	constructor() {
		console.log(Person.MAX_AGE) // 通过类访问
	}
}
new Person() // 150
Person.MAX_AGE = 149 // 可以修改该属性
new Person() // 149

Typscript中增加了public、protected、private修饰符,访问权限public > protected > private:

使用private时,意味着该成员是私有的,会控制成员仅能在类内部访问

class Person {
  private category: string = 'human'
  constructor() {
    return this
  }
  getCategory() {
    console.log('getCategory:', this.category)
  }
}
let p = new Person()
p.getCategory() // getCategory:human
console.log(p.catagory) // Property 'catagory' is private and only accessible within class 'Person'.ts(2341)

继承的类也无法访问private成员:

class Person {
  private category: string = 'human'
  constructor() {
    return this
  }
  getCategory() {
    console.log('getCategory:', this.category)
  }
}
class Student extends Person {
  school: string = '春田花花幼儿园'
  constructor() {
    super()
    return this
  }
}
let p1 = new Student()
console.log(p1.school)// 春田花花幼儿园
console.log(p1.category)// Property 'category' is private and only accessible within class 'Person'.ts(2341)
p1.getCategory()// getCategory: human

如果该属性是protected的,子类继承时子类内部可以访问父类的protected成员,但是外部或者实例也无法访问:

class Person {
  protected category: string = 'human'
  constructor() {
    return this
  }
  getCategory() {
    console.log('getCategory:', this.category)
  }
}
class Student extends Person {
  school: string = '春田花花幼儿园'
  constructor() {
    super()
    return this
  }
  getCategory2() {
    console.log('Student访问内部成员:', this.category)
  }
}
let p1 = new Student()
p1.getCategory2()//Student访问内部成员: human
console.log(p1.category)//Property 'category' is protected and only accessible within class 'Person' and its subclasses.ts(2445)

11、implements(实现)

类继承类、接口继承接口用extends,类实现接口、类实现抽象类用implements

interface Person {
  name: string,
  walk: () => void
}
class Joe implements Person {
  name: string = 'Joe'
  constructor() {
    return this
  }

  walk() {
    console.log('I can walk')
  }
}
new Joe().walk()// I can walk

 12、abstract(抽象)

抽象类一般用于继承,而不能直接使用

abstract class Animal {
  name: string = 'Animal'
  constructor() {
    return this
  }
}

console.log(new Animal())// Cannot create an instance of an abstract class.ts(2511)
abstract class Animal {
  name: string = 'Animal'
}
class Cat extends Animal {
  name: string = 'Cat'
  constructor() {
    super()
    return this
  }
  say() {
    console.log('I am Cat')
  }
}
console.log(new Cat().say())// I am Cat

abstract与interface的区别:

  • interface用于定义对象的结构,描述对象包含的属性和方法,无法设置值和实现方法;
  • abstract用于作为子类的基类,描述基类包含的属性或方法,允许包含通用方法的实现或属性的定义;
  • interface可被interface继承,也可以被class实现
  • abstract可以被abstract继承,也可以被class继承

exends和implements的区别:

  • extends可用于子类继承父类,interface继承interface
  • implements用于class对interface的实现

13、装饰器

装饰器的作用类似于房屋建筑的装饰物,在不影响建筑物的情况下增加一些装饰作用,例如灯具、挂饰。

装饰器的优点:

  1. 代码复用:装饰器允许你把增强功能封装到独立的模块中,从而避免代码重复。
  2. 解耦合:通过装饰器可以将通用的功能和具体的实现分离,从而使得代码更加灵活和可维护。
  3. 增强功能:可以通过装饰器来添加额外功能,比如日志记录、权限验证、缓存等。

 使用装饰器前需要在tsconfig.json中开启装饰器编译:

"compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
}

例如下面这个例子:

// 日志装饰器
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  // target指向User
  // propertyKey指向装饰器装饰的方法名,这里的值为"updateProfile"
  // descriptor指向描述当前装饰器装饰的方法属性描述符
  // descriptor.value指向当前装饰器装饰的方法,这里指向updateProfile方法
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
      // 重新设置装饰器装饰的方法,增加日志代理
      console.log(`Method ${propertyKey} was called with args: ${JSON.stringify(args)}`);
      return originalMethod.apply(this, args);
  };
}

class User {
  @Log
  updateProfile(name: string, age: number) {
      console.log(`Updating profile for ${name}, age ${age}`);
  }
}

const user = new User();
// 调用updateProfile方法时会触发Log方法这个钩子,输入代理日志
user.updateProfile("Alice", 30);

14、使用typescript创建俄罗斯方块游戏

demo:Tetris: 俄罗斯方块

类设计说明

俄罗斯方块游戏的类设计分为以下几个类:

1、View:游戏视图的基类,实现了初始化游戏区域数据、清空游戏区域、绘制方块等方法

2、DataView:游戏数据视图,继承了View类,负责绘制已落下的俄罗斯方块,消除一行时通知UI更新得分

3、BlockView:方块视图,叠加在DataView上方的俄罗斯方块数据视图,继承了View类,绘制下落的俄罗斯方块,方块落下时如果无法继续下落,会把方块同步到DataView中,再绘制下一个俄罗斯方块

4、Preview:预览视图,负责绘制下一个俄罗斯方块,告诉玩家下一个俄罗斯方块是什么

5、Interaction:交互类,为UI类提供接口操作BlockView的方块移动、旋转等

6、UI类:负责创建游戏UI视图,调用Interaction的方法操作BlockView的方块移动

7、Block类:俄罗斯方块的基类,提供了方块旋转、随机方法

8、Block1/Block2/Block3等类:描述不同俄罗斯方块的不同数据内容,生成的Block会添加到BlockView中

参考资料

typescript超详细入门教程(上)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值