一、 泛型
泛型在保证类型安全(不丢失类型信息)的同时,可以让函数等与多种不同的类型一起工作,灵活可复用
1. 泛型函数
// 1. 参数类型
function 函数名<Type>(形参:Type){
}
// 2. 返回值类型
function 函数名<Type>(形参:Type):Type{
}
function getData<T>(args:T){
return [args]
}
interface IDog{ //接口
name:string
}
class Father{ //类
name:string
age:number
constructor(name:string,age:number) {
this.name = name
this.age = age
}
}
getData<number>(1)
getData<string>('1')
getData<boolean>(false)
getData<number[]>([1,2,3])
getData<IDog>({name:'asd'}) //接口
getData<Father>(new Father('qaq',2)) //类
2. 泛型约束
语法:function 函数<Type extends 约束的类型>(){}
// 联合类型
function Mdy<T extends number | string>(args:T){
return args
}
Mdy<number>(1)
Mdy<string>('2')
//枚举类型
function Ddd<T extends Color>(args:T){
return args
}
Ddd<Color>(Color.White)
//接口
interface IPerson{
name:string
}
function Aaa<T extends IPerson>(args:T){
return args
}
Aaa<IPerson>({name:'aaa'})
//类
class Dog{
name:string
constructor(name:string) {
this.name = name
}
}
function Sss<T extends Dog>(args:T){
return args
}
Sss<Dog>(new Dog('大黄'))
3. 多个泛型参数
语法:function funcA<T, T2>(param1: T, param2: T2) {}
function arr<T1,T2>(a:T1,b:T2){
console.log(''+a,b)
}
arr<string,boolean>('1',false)
// 多个泛型约束
function Arr<T1 extends string,T2 extends number>(c:T1,d:T2){
console.log(''+c,d)
}
Arr<string,number>('2',1)
练一练
使用泛型函数,接受一个参数,根据参数类型 随机生成一个100以内的宽度值。 类型可以是字符串:'90%',也可以是数值:90。
需求:
- 需要对泛型参数进行类型约定为: string | number
- 需要在方法体内部判断形参类型判断 typeof 形参
- 根据形参类型来决定是返回字符串还是返回数字
function Weidth<T extends string|number>(args?:T){//设置为可选参数
let ran = Math.floor(Math.random()*101) //随机生成0-100,向下取整
if(typeof args == 'number'){ //判断类型 typeof(判断类型)
return ran
}else {
return `${ran}%` //模板字符串
}
}
console.log(''+Weidth<number>())
console.log(''+Weidth<string>())
4. 泛型接口
语法:interface 接口名<Type>{ // 内部使用Type }
interface IData<T>{
code:number
msg:string
data:T
}
let data1:IData<string[]>={
code:200,
msg:'ok' ,
data:['1']
} //console.log只能打印字符串 ,对象需要JSON.stringify()才能打印
console.log('',JSON.stringify(data1))
5. 泛型类
语法:class 类名<Type>{ // 内部可以使用 Type }
class Person <T> {
id: T
constructor(id: T) {
this.id = id
}
getId() {
return this.id
}
}
// 使用
let p = new Person<number>(10)
二、 工具类型
1. Partial<Type>
基于传入的Type类型构造一个【新类型】,将Type的所有属性设置为可选。
type 新类型 = Partial<接口>
type 新类型 = Partial<类>
class Person {
name: string = ''
age: number = 0
friends: string[] = []
}
type ParPerson = Partial<Person>
// 因为都是可选的,可以设置为空对象
let p: ParPerson = {}
2. Required<Type>
基于传入的Type类型构造一个【新类型】,将 Type 的所有属性设置为必填
type 新类型 = Required<接口>
type 新类型 = Required<类>
3. Readonly<Type>
基于 Type构造一个【新类型】,并将Type 的所有属性设置为readonly
type 新类型 = Readonly<接口>
type 新类型 = Readonly<类>
4. Record<Keys,Type>
构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一种类型的属性映射到另一种类型。
let person:Record<string,string> = {'name':'张三'}
let name = person['name'] //张三
let person1:Record<string,number> = {'age':20}
let age = person1['age'] //20
三、 空安全
默认情况下,ArkTS中的所有类型都是不可为空的。如果要设置为空,需要进行特殊的处理,并且在获取 可能为空的值的时候也需要特殊处理
1. 联合类型设置为空
// 通过联合类型设置为空
let x: number | null = null
x = 1 // ok
x = null // ok
2. 非空断言运算符
后缀运算符! 可用于断言其操作数为非空。
let x: number | null
let y: number
//y = x + 1; // 编译时错误:无法对可空值作加法❌
y = x! + 1; // 通过非空断言,告诉编译器 x不为 null✔️
3. 空值合并运算符
空值合并二元运算符?? 用于检查左侧表达式的求值是否等于null。如果是,则表达式的结果为右侧表达式;否则,结果为左侧表达式。
class Person {
name: string | null = null
getName(): string {
// return this.name != null ? this.name : ''
// 等同于 如果 name不为空 就返回 name 反之返回 ''
return this.name ?? '' // = return this.name != null ? this.name : ''
}
}
4. 可选链
在访问对象属性时,如果该属性是undefined或者null,可选链运算符会返回undefined。
interface IPerson{
name:string
dogAge?:number
}
let person:IPerson = {name:'小明'}
// console.log(person.dogAge.toString()) //报错
console.log(person.dogAge?.toString()) //正确
if(person.dogAge){ // 判断person.dogAge是否为空
console.log(person.dogAge.toString()) //不为空
}
四、 模块化
概念:
- 把一个大的程序拆分成【互相依赖】的若干小文件
- 这些小文件还可以通过【特定的语法】组合到一起
- 这个过程称之为【模块化】
逻辑都写在一起,并且越写越多------------------模块化之后 (😄)
1. 默认导出和导入
// 默认导出
export default 需要导出的内容
// 默认导入
import xxx from '模块路径'
试一试:
- ets 目录下创建【目录 utils】
- models 目录下【创建 tools.ets】文件
- tools.ets中定义一个 let num:number = 10
- 使用【默认导出】语法,将其导出
- 页面Index.ets中【导入 tools.ets 中导出的内容】 并使用
//目录:src/main/ets/utils/tools.ets
let num:number = 10
// 默认导出num
export default num
//目录:src/main/ets/pages/Index.ets
// 导入模块
import num from '../utils/tools'
// 使用
console.log(num.toString())
2. 相对路径
. 表示当前文件所在文件夹 ./
.. 表示当前文件的上一级文件夹 ../
3. 按需导出和导入
导出:
// 按需导出写法1:逐个导出单个特性
export let name1 = '齐天大圣'
export const PI = Math.PI
export function sayHi(){ console.log('我是齐天大圣孙悟空') }
export interface iPerson {
name:string
}
export class tools {
static getRandom(num: number) {
return Math.floor(Math.random() * num)
}
}
//方法2:一次性导出
let name1 = '齐天大圣'
const PI = Math.PI
function sayHi(){ console.log('我是齐天大圣孙悟空') }
interface iPerson {
name:string
}
class tools {
static getRandom(num: number) {
return Math.floor(Math.random() * num)
}
}
export { name1, PI, sayHi,iPerson,tools }
导入:
import {name1, PI, sayHi,iPerson,tools as utils} from '../utils/tools'
4. 全部导入
!!!导出部分不需要调整,调整导入的语法即可
// 一次性全部导入
import * as utils from '../utils/tools'
let person:utils.iPerson = {name:'孙悟空'}
console.log(utils.name1,utils.PI,person,utils.tools.getRandom(100))
练一练
需求:
- 请定义一个模块colorManager.ets,导出一个类,里面有一个静态方法,随机生成颜色
- 在index.ets这个模块中 导入colorManager.ets中的随机颜色生成方法
- 在.backgroundColor()上使用