鸿蒙开发——搞懂ArkTS语言中什么是接口?泛型?类型断言?

今日核心知识点:

  1. 接口、泛型、工具类型、空安全、模块化

1. 接口

接口可以用来定义类型

1.1. 接口继承

接口继承使用的关键字是 extends

//定义接口 
interface IFood {
  name: string
}
​
//继承接口
interface IVegetable extends IFood {
  color: string
}
​
//进行类型限制
const flower: IVegetable = {
  name: '西兰花',
  color: '黄绿色'
}

1.2. 接口实现 (类来实现接口)

可以通过接口结合 implements 来限制 类 必须要有某些属性和方法

//定义接口,定义属性、方法
interface IDog {
  name: string
  bark: () => void
}
​
//定义类 
// 1. 实现接口
// 2. 额外添加 属性 、方法
class Dog implements IDog {
  name: string = ''
  food: string = ''
​
  bark() {
​
  }
}

2. 泛型

泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用

通俗一点就是:类型是可变的!

为了更好的理解为什么需要 类型可变,咱们来一个具体的例子:一个函数,将传入的参数,转换成数组返回

// 1. 字符串
function identityStr(arg: string) {
  return [arg]
}
​
// 2. 数字
function identityNum(arg: number) {
  return [arg]
}
​
// 3. 布尔
function identityBoolean(arg: boolean) {
  return [arg]
}

2.1. 泛型函数

顾名思义就是,泛型和函数结合到一起使用

Type 是泛型参数的名字,类似于之前的形参,

  • 可以自行定义,有意义即可

  • 首字母大写

  • 参见泛型参数名 T、Type

// 定义泛型参数 Type,后续可以使用类型的位置均可以使用比如: 形参、函数内部、返回值
​
// 1. 参数类型
function 函数名<Type>(形参:Type){
}
​
// 2. 返回值类型
function 函数名<Type>(形参:Type):Type{
}

使用泛型改写上一节的代码

// 函数定义
function identity<Type>(arg: Type) {
  return [arg]
}
​
identity<string>('123')
identity<number>(123)
identity<boolean>(false)
identity<string[]>(['1', '2', '3'])

2.2. 泛型约束

如果开发中不希望任意的类型都可以传递给 类型参数 ,就可以通过泛型约束来完成

核心步骤:

  1. 定义用来约束的 接口(interface)

  2. 类型参数通过 extends 即可实现约束

interface 接口{
  属性:类型
}
function 函数<Type extends 接口>(){}
​
// 后续使用函数时,传入的类型必须要有 接口中全部的属性

试一试:

  • 实现泛型函数,内部打印 传入 【参数的 length】 属性

  • 通过泛型约束,保证传入的参数 【length 属性】

interface ILengthwise {
  length: number
}
​
function identity<Type extends ILengthwise>(arr: Type) {
  console.log(arr.length.toString())
}
​
// 使用的时候 只要有 length 属性即可
identity([1, 2, 3, 4]) // 数组有 length 属性 正常运行
identity('1234') // 字符串也有 length 属性 正常运行
​
// identity(124) // 数值没有 length 属性 报错
​
class Cloth implements ILengthwise {
  length: number = 10
}
​
class Trousers {
  length: number = 110
}
​
identity(new Cloth()) // Cloth 有 length 属性 正常运行
identity(new Trousers()) // Trousers 有 length 属性 正常运行
interface IFood {
  food: string
}
​
function logFood<T extends IFood>(param: T) {
  console.log('吃的挺好:', param.food)
}
​
class Lunch {
  food: string
​
  constructor(food: string) {
    this.food = food
  }
}
​
const l = new Lunch('醉面')
logFood(l)

如果要加更多的约束,只需要给作为约束的接口 添加更多的内容即可。

2.3. 多个泛型参数

日常开发的时候,如果有需要可以添加多个 类型变量,只需要定义并使用 多个类型变量即可

function funcA<T, T2>(param1: T, param2: T2) {
  console.log('参数 1', param1)
  console.log('参数 2', param2)
}
​
funcA<string, number>('西兰花', 998)
funcA<string[], boolean[]>(['西兰花'], [false])

根据需求,可以添加任意个类型变量,添加约束的方式也和之前的章节一致

2.4. 泛型接口

定义接口时结合泛型,那么这个接口就是 泛型接口

//定义泛型接口,接受泛型参数
interface IdFunc<Type> {
  id: (value: Type) => Type
  ids: () => Type[]
}

// 接口定义
// 1. ids: 数组,类型使用 **泛型参数** 替代
// 2. getIds:函数,返回数组,**参数类型**和**返回值类型** 使用泛型参数替代
let obj: IdFunc<number> = {
  id(value) { return value },
  ids() { return [1, 3, 5] }
}

2.5. 泛型类

和泛型接口类似,如果定义类的时候结合泛型,那么这个类就是 泛型类

// 定义泛型类,接收泛型参数  T
class Person <T> {
//定义属性,类型为 T
  id: T   

//构造函数,接收属性,类型为T
  constructor(id: T) {
    this.id = id
  }

//方法,返回属性,类型为T
  getId(): T {
    return this.id
  }
}

// 使用
let p = new Person<number>(10)

3. 工具类型

ArkTS提供了4 中工具类型,来帮组我们简化编码

3.1. Partial<Type>

基于传入的Type类型构造一个【新类型】,将Type的所有属性设置为可选。

type 新类型 = Partial<接口>
type 新类型 = Partial<类>

// 后续使用新类型即可

试一试:

  1. 定义 类,添加属性

  2. 通过 Partial 基于上一步定义的类,返回新类型

  3. 使用 新类型,确认结果

class Person {
  name: string = ''
  age: number = 0
  friends: string[] = []
}

type ParPerson = Partial<Person>

// 因为都是可选的,可以设置为空对象
let p: ParPerson = {}

3.2. Required<Type>

基于传入的Type类型构造一个【新类型】,将 Type 的所有属性设置为必填

type 新类型 = Required<接口>
type 新类型 = Required<类>

// 后续使用新类型即可

试一试:

  1. 定义 类,添加属性,全部设置为可选

  2. 通过 Required 基于上一步定义的类,返回新类型

  3. 使用 新类型,确认是否转为必填属性

class Person {
  name?: string
  age?: number
  friends?: string[]
}

type RequiredPerson = Required<Person>

// 都是必须得属性,必须设置值
let p: Required<Person> = {
  name: 'jack',
  age: 10,
  friends: []
}

3.3. Readonly<Type>

基于 Type构造一个【新类型】,并将Type 的所有属性设置为readonly

type 新类型 = Readonly<接口>
type 新类型 = Readonly<类>

// 后续使用新类型即可

试一试:

  1. 定义 类,添加属性

  2. 通过 Readonly 基于上一步定义的类,返回新类型

  3. 使用 新类型,确认结果

class Person {
  name: string = ''
  age: number = 0
}

type ReadonlyIPerson = Readonly<Person>

const p: ReadonlyIPerson = {
  name: 'jack',
  age: 10
}

p.name = 'rose' // 报错 属性全部变成只读

3.4. Record<Keys,Type>

构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一种类型的属性映射到另一种类型。

interface CatInfo{
  age:number
  breed:string 
}

// 联合类型
type CatName = "miffy" | "boris" | "mordred";

// 通过Record 构建新的对象类型
// 属性名:必须是CatName 中的值
// 属性值:必须是CatInfo 类型
type Ctype = Record<CatName, CatInfo> //  CatInfo 也可以是class 

const cats: Ctype = {
  'miffy': { age: 5, breed: "Maine Coon" },
  'boris': { age: 5, breed: "Maine Coon" },
  'mordred': { age: 16, breed: "British Shorthair" },
};

4. 空安全

默认情况下,ArkTS中的所有类型都是不可为空的。如果要设置为空,需要进行特殊的处理,并且在获取 可能为空的值的时候也需要特殊处理

4.1. 联合类型设置为空

let x: number = null;    // 编译时错误
let y: string = null;    // 编译时错误
let z: number[] = null;  // 编译时错误

通过联合类型指定为空即可,使用时可能需要判断是否为空

// 通过联合类型设置为空
let x: number | null = null;
x = 1;    // ok
x = null; // ok
// 取值的时候,根据需求可能需要考虑 屏蔽掉空值的情况
if (x != null) { /* do something */ }

4.2. 非空断言运算符

后缀运算符! 可用于断言其操作数为非空。

应用于空值时,运算符将抛出错误。否则,值的类型将从T | null更改为T:

let x: number | null = 1;
let y: number
y = x + 1;  // 编译时错误:无法对可空值作加法
y = x! + 1; // 通过非空断言,告诉编译器 x不为 null

4.3. 空值合并运算符

空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。

换句话说,a ?? b等价于三元运算符a != null ? a : b。 undefine==null => true

在以下示例中,getNick方法如果设置了昵称,则返回昵称;否则,返回空字符串:

class Person {
  name: string | null = null

  getName(): string {
    // return this.name === null ? '' : this.name
    // 等同于 如果 name不为空 就返回 name 反之返回 ''
    return this.name ?? ''
  }
}

4.4. 可选链

在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。

class Dog {
  bark() {
    console.log('啊呜~')
  }
}
​
class Person {
  name?: string
  dog?: Dog
​
  constructor(name: string, dog?: Dog) {
    this.name = name
    this.dog = dog
  }
}
​
const p: Person = new Person('jack')
​
// p.dog.bark()// 报错 dog 可能为空
​
// 逻辑判断
if (p.dog) {
  p.dog.bark()
}
​
// 当 dog不为null 或 undefined 时 再调用 bark 并且不会报错
p.dog?.bark()

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值