TypeScript高级类型(class类、类型兼容性、交叉类型)

TypeScript高级类型

class 类

class 类的基本使用

TS 中的 class,不仅提供了 class 的语法功能,也作为一种类型存在。
   class Person{
    age:number 
    gender='男' //根据值会进行类型推断
    }
    //p的类型是Person
    const p=new Person()
    p.age
    p.gender
    

class 的构造函数

  1. 成员初始化(比如,age: number)后,才可以通过 this.age 来访问实例成员。
  2. 需要为构造函数指定类型注解,否则会被隐式推断为 any;构造函数不需要返回值类型。
class Person{
  age:number
  gender:string

  constructor(age:number,gender:string){
     this.age=age
     this.gender=gender
  }
}

const p =new Person(18,'男')

console.log(p.age)
console.log(p.gender)

class 的实例方法

方法的类型注解(参数和返回值)与函数用法相同。

class Point {
	  x=1
	  y=2
    scale(n:number):void{
      this.x*=n
      this.y*=n
    }
}
const p = new Point()

p.scale(20)
console.log(p.x,p.y)

class 的继承

说明:JS 中只有 extends,而 implements 是 TS 提供的。

extendsclassclass的关系
implements 是一个class和一个接口的关系
1. extends
class Animal{
  move(){
     console.log('走两步')
  }
}

class Dog extends Animal{
  name='小狗'
  bark(){
     console.log(this.name,'叫')
  }
}

 const d=new Dog()

 d.move()
 d.bark()
1. 通过 extends 关键字实现继承。
2. 子类 Dog 继承父类 Animal,则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。
2. implements
 interface Singable{
  sing():void
}
//extends 是class与class的关系
//implements 是一个class和一个接口的关系

class Person implements Singable{
  sing(): void {
      console.log('你是我的小苹果')
  }
}
 const Ns=new Person()
 Ns.sing()
 
1. 通过 implements 关键字让 class 实现接口。
2. Person 类实现接口 Singable 意味着,Person 类中必须提供 Singable 接口中指定的所有方法和属性。

类的成员可见性

1. public(公有的)

public:表示公有的、公开的,公有成员可以被任何地方访问,默认可见性。

class Animal{
  public move(){ //public可直接省略
    console.log('走两步')
  }
}
2. protected(受保护的)

protected:表示受保护的,仅对其声明所在类和子类中可见。
实例化对象不能访问 protected 受保护的方法

   class Animal{
    protected move(){
        console.log('走两步')
    }
    }

    class Dog extends Animal{
    bark(){
        this.move() //子类访问
    }
    }
    const dog =new Dog()
    //实例对象不能访问 protected 受保护的方法
    //dog.move()
    dog.bark()
    
3. private(私有的)

private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。

  class Animal{
 private __run__(){
  console.log('Animal 内部辅助函数')

 }
  protected move(){
    console.log('走两步')
    this.__run__()
  }
  run(){
    //只在当前类中可见
    //对子类和实例对象不可见
    this.__run__()
  }
}

class只读修饰符(readonly)

readonly:表示属性只读,只能构造函数中赋一次值、

// class Person{
//   //readonly表示只读属性
//  readonly age: number=18
//   constructor(age:number){
//     this.age=age
//   }
//   setAge(){
//     //this.age是只读属性、不可修改值
//     //this.age=20
//   }
// }
// const aa=new Person(18)
//  //aa.age=22//无法分配到 "age" ,因为它是只读属性

class Person1{
  // 只要是readonly修饰的属性、必须手动指定明确的类型
  //否则根据类型推论可能不是我们想要的类型
  readonly age=18
  constructor(age:number){
    //this.age=age//不能将类型“number”分配给类型“18”
  }
}

interface IPerson{
  readonly name:string
}
let obj:IPerson={
  name:'jack'
}

//obj.name='re'//无法分配到 "name" ,因为它是只读属性

let obj1:{readonly name:string}={
  name:'jack'
}

//obj.name='re22'//无法分配到 "name" ,因为它是只读属性

  1. 使用 readonly 关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法。
  2. 注意:只要是readonly修饰的属性、必须手动指定明确的类型、否则根据类型推论可能不是我们想要的类型。
  3. 接口或者 {} 表示的对象类型,也可以使用 readonly。

类型兼容性

TS 采用的是结构化类型系统,也叫做 duck typing(鸭子类型),类型检查关注的是值所具有的形状。 也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。

类的兼容性

class Point{x:number; y:number}
class Point2D{x:number; y:number}

class Point3D{x:number; y:number;z:number}

const p:Point=new Point2D()

const p1:Point=new Point3D() //成员多的可以赋值给少的

解释:

  1. Point 和 Point2D 是两个名称不同的类。
  2. 变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。
  3. 因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。
  4. 但是,如果在 Nominal Type System (标明类型系统)中(比如,C#、Java 等),它们是不同的类,类型无法兼容。

接口兼容性

接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。
成员多的可以赋值给少的

interface Point{x:number; y:number}
interface Point2D{x:number; y:number}
interface Point3D{x:number; y:number;z:number}

let p1:Point
let p2:Point2D
let p3:Point3D
//正确
// p1=p2
// p2=p1
// p1=p3
//错误演示:类型 "Point" 中缺少属性 "z",但类型 "Point3D" 中需要该属性。
//p3=p1

//class 和 interface 之间也可以兼容。
class Point4D{ x:number; y:number;z:number}
p2=new Point4D

函数兼容性

1. 参数个数
 参数少的可以赋值给多的
type F1=(a:number)=>void
type F2=(a:number,b:number)=>void

let f1:F1
let f2:F2

f2=f1 //少的赋值给多的
//f1=f2 //错误:多的不能给少的

// let arr=['a','b','c']
// arr.forEach(item=>{})
// arr.forEach((item,index)=>{})
// arr.forEach((item,index,arr)=>{})

在 JS 中省略用不到的函数参数实际上是很常见的,这样的使用方式,促成了 TS 中函数类型之间的兼容性。

2. 参数类型
参数类型,相同位置的参数类型要相同或兼容。
//原始类型
// type F1=(a:number)=>void
// type F2=(a:number)=>void
// let f1:F1
// let f2:F2
// f1=f2 //原始类型相同

//对象类型
interface Point2D{ x:number;y:number}
interface Point3D{x:number;y:number;z:number}

type F2=(p:Point2D)=>void
type F3=(p:Point3D)=>void

let f2:F2
let f3:F3

f3=f2 //少的参数赋值给多的

3. 返回值类型
 返回值类型,只关注返回值类型本身即可
 //原始类型 返回值类型相同即可
  type F5=()=>string
  type F6=()=>string
  let f5:F5
  let f6:F6
  f5=f6
  f6=f5

  //对象类型
  type F7=()=>{name:string}
  type F8=()=>{name:string,age:number}
  let f7:F7
  let f8:F8
  f7=f8 //对象属性多的可以赋值给少的
  //f8=f7//错误
  

交叉类型 &

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。

基本使用

interface Person{ name:string,say():number}
interface Content{ phone:string}

type PersonDetail=Person&Content

let obj:PersonDetail={
  name:'李寒松',
  phone:'18773568***',
  say(){return 1}
}

交叉类型与接口继承的对比

相同点:都可以实现对象类型的组合。
不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。

// interface A{
//   fn:(value:number)=>string
// }
// interface B extends A{
//   fn:(value:number)=>string
// }

interface A{
  fn:(value:number)=>string
}
interface B extends A{
  fn:(value:string)=>string
}

type C=A&B
//&相当于这种类型
let c:C={
  fn(value:number|string){
    return ''
  }
}
说明:以上代码,接口继承会报错(类型不兼容);交叉类型没有错误,
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值