0


Typescript面向对象---下篇

目录

ts专栏 ===> 🌈 typescript入门到拔高🌈(持续更新中…)

接口

接口的作用类似于抽象类,不同点在于

接口中的所有方法和属性都是没有实值的

,换句话说

接口中的所有方法都是抽象方法

。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)
接口类型的对象 :

  1. 多了或者少了属性是不允许的
  2. 可选属性: ?
  3. 只读属性: readonly

现在我们做一个需求帮助大家更好的理解接口:

创建人的对象, 需要对人的属性进行一定的约束

id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有

// 定义人的接口interfaceIPerson{
  id:number
  name:string
  age:number
  sex:string}const person1: IPerson ={
  id:1,
  name:'james',
  age:37,
  sex:'男'}

注意: 类型检查器会查看对象内部的属性是否与 IPerson 接口描述一致,

如果不一致就会提示类型错误

可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。

interfaceIPerson{
  id:number
  name:string
  age:number
  sex?:string}

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个

?

符号

可选属性的好处之一是

可以对可能存在的属性进行预定义

,好处之二是

可以捕获引用了不存在的属性时的错误
const person2: IPerson ={
  id:1,
  name:'james',
  age:37// sex: '男' // 可以没有}

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:

interfaceIPerson{readonly id:number
  name:string
  age:number
  sex?:string}

一旦赋值后再也不能被改变了。

const person2: IPerson ={
  id:2,
  name:'harden',
  age:33// sex: '男' // 可以没有// score: 12 // error 没有在接口中定义, 不能有}
person2.id =13// error
  • readonly vs const- 最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly

函数类型

接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。

为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。

/*
接口可以描述函数类型(参数的类型与返回的类型)
*/interfaceSearchFunc{(source:string, subString:string):boolean}

这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

const mySearch:SearchFunc=function(source:string, sub:string):boolean{return source.search(sub)>-1}console.log(mySearch('abcd','bc'))

类类型

定义类时,可以使类实现一个接口,实现接口就是使类满足接口的要求,实现接口需要使用一个关键字

implements

类实现接口

interfaceAlarm{alert():any}interfaceLight{lightOn():voidlightOff():void}classCarimplementsAlarm{alert(){console.log('Car alert')}}

一个类可以实现多个接口

classCar2implementsAlarm, Light {alert(){console.log('Car alert')}lightOn(){console.log('Car light on')}lightOff(){console.log('Car light off')}}

接口继承接口

和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

interfaceLightableAlarmextendsAlarm, Light {}

泛型

定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型便能够发挥作用。

  • 举例functiontest(arg:any):any{return arg;}
  • 上例中,test函数有一个参数类型不确定,但是能确定的是其返回值的类型和参数的类型是相同的,由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的,首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型,这样的话跟js还有什么区别呢?
  • 使用泛型:functiontest<T>(arg:T):T{return arg;}
  • 这里的 <T> 就是泛型,T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型。所以泛型其实很好理解,就表示某个类型。
  • 那么如何使用上边的函数呢? - 方式一(直接使用): - test(10)- 使用时可以直接传递参数使用,类型会由TS自动推断出来,但有时编译器无法自动推断时还需要使用下面的方式- 方式二(指定类型): - test<number>(10) - 也可以在函数后手动指定泛型

多个泛型参数的函数

一个函数可以定义多个泛型参数,泛型间使用逗号隔开:

functiontest<T,K>(a:T, b:K):K{return b;}test<number,string>(10,"hello");

使用泛型时,完全可以将泛型当成是一个普通的类去使用

泛型接口

在定义接口时, 为接口中的属性或方法定义泛型类型在使用接口时, 再指定具体的泛型类型

interfaceIbaseCRUD<T>{
  data:T[]add:(t:T)=>voidgetById:(id:number)=>T}classUser{
  id?:number//id主键自增
  name:string//姓名
  age:number//年龄constructor(name, age){this.name = name
    this.age = age
  }}classUserCRUDimplementsIbaseCRUD<User>{
  data: User[]=[]add(user: User):void{
    user ={...user, id: Date.now()}this.data.push(user)console.log('保存user', user.id)}getById(id:number): User {returnthis.data.find(item => item.id === id)}}const userCRUD =newUserCRUD()
userCRUD.add(newUser('tom',12))
userCRUD.add(newUser('tom2',13))console.log(userCRUD.data)

泛型类

在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型

classGenericNumber<T>{
  zeroValue:Tadd:(x:T, y:T)=>T}let myGenericNumber =newGenericNumber<number>()
myGenericNumber.zeroValue =0
myGenericNumber.add=function(x, y){return x + y
}let myGenericString =newGenericNumber<string>()
myGenericString.zeroValue ='abc'
myGenericString.add=function(x, y){return x + y
}console.log(myGenericString.add(myGenericString.zeroValue,'test'))console.log(myGenericNumber.add(myGenericNumber.zeroValue,12))

泛型约束

如果我们直接对一个泛型参数取

length

属性, 会报错, 因为这个泛型根本就不知道它有这个属性

// 没有泛型约束functionfn<T>(x:T):void{// console.log(x.length)  // error}

我们可以使用泛型约束来实现

interfaceLengthwise{
  length:number}// 指定泛型约束functionfn2<Textends Lengthwise>(x:T):void{console.log(x.length)}

我们需要传入符合约束类型的值,必须包含必须 length 属性:

fn2('abc')// fn2(123) // error  number没有length属性

使用

T extends Lengthwise

表示泛型T必须是Lengthwise的子类.

小结

到这里,面向对象的知识就到尾声了,同时typescript的知识暂停一段落,希望各位能够在这个系列中学到一些东西,主要是需要会应用,一切在实践过后才能够充分掌握。


本文转载自: https://blog.csdn.net/m0_52040370/article/details/126249371
版权归原作者 前端小白在前进 所有, 如有侵权,请联系我们删除。

“Typescript面向对象---下篇”的评论:

还没有评论