学习TypeScript第三天

本文深入探讨了TypeScript的高级类型,包括类的使用、类型兼容性、交叉类型以及接口和泛型。详细介绍了类的构造函数、实例方法、继承和可见性修饰符,同时讲解了类型兼容性的规则,如结构化类型系统的特性,以及函数和接口之间的兼容性。此外,还讨论了交叉类型如何组合多个类型为一个类型。
摘要由CSDN通过智能技术生成

TS的高级类型:

class,类型兼容,交叉类型,泛型和keyof,索引签名类型和索引查询类型,映射类型

(1)class类:

TS全面支持ES2015中引入的关键字,并为其添加了类型注解和其他语法(比如:可见性修饰等)

class 基本使用:

根据TS的类型推论,可以知道person类的实例对象p的类型是Person;

TS 中的class,不仅提供了class 的语法功能,也作为一种类型的存在

class Person {}
const p = new Person()
// 鼠标放在p上面显示 const p: Person

实例初始化:

class Person {
    age: number // 第一种方式,声明成员age,类型为number,没有初始值
    gender = '男' // 第二种方式,声明成员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, '男')
// p.age, p.gender 可以获取相关数据

类的实例方法在TS中的使用:

class Point {
    x = 10
    y = 10
    // 方法的类型注解(参数和返回值)与函数用法相同
    scale(n: number): void {
        this.x *= n
        this.y *= n
    }
}

class类的继承:

1、extends(继承父类)--->两个类之间的关系

2、implements(实现接口)--->一个类和一个接口之间的关系

JS中只有extends,而implements是TS提供的

继承父类:

(1)通过extends关键字实现继承

(2)子类Dog继承父类Animal,则Dog的实例对象dog就同时拥有了父类Animal和子类Dog的所有属性和方法

class Animal {
    move() {console.log('move')}
}
class Dog extends Animal {
    name: 'hh'
    bark() {console.log('汪汪')}
}
const dog = new Dog()

class 类中实现接口:

interface Singable {
    name: string
    sing(): void
}
class Person implements Singable {
    name: 'jack'
    sing() {
        console.log('sing')
    }
}
/*
(1)通过implements关键字让class实现接口
(2)Person类实现接口Singable意味着Person类中必须提供Singable的所有方法和属性

class类的可见性修饰符:public(公有的),protected(受保护的),private(私有的)

类成员的可见性:可以使用TS来控制class的方法或属性,对于class外的代码是否可见

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

class Animal {
    public move() {
        console.log('move')
    }
}

(1)在类属性或方法前面添加public关键字,来修饰该属性或方法是共有的

(2)public是默认可见性,所以,可以直接省略

2、protected:表示受保护的,仅在声明的类和子类中(非实例对象)可见

class Animal {
    protected move() {
        console.log('move')
    }
}
class Dog extends Animal {
    bark() {
        console.log('汪')
        this.move()
    }
}

(1)在类属性或方法面前添加protected关键字,来修饰改属性或方法是受保护的

(2)在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见

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

class Animal {
    private move() {
        console.log('move')
    }
    walk() {
        this.move()
    }
}

(1)在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的

(2)私有的属性或方法只在当前类中可见,对子类和实例对象也都是不可见的

readonly只读修饰

除了可见性修饰符外,还有一个常见的修饰符就是:readonly(只读修饰符)

readonly:表示只读,用来防止在构造函数之外对属性进行赋值

class Person {
    // 这个18是默认值,也可以不设置
    // 只要是readonly修饰的属性,必须手动提供明确的类型
    readonly age: number = 18
    constructor(age: number) {
        // 在constructor构造函数中为属性赋值
        this.age = age
    }
}

(1)使用readonly关键字修饰该属性是只读的,只能修饰属性不能修饰方法

(2)属性age后面的类型注解(比如这个number),如果不加,则age的类型为18(就是字面量类型了)

(3)接口或者{}表示的对象类型,也可以使用readonly

类型兼容性:(TS中类型系统中的一个特性)

有两种系统,一个是结构化类型系统,一个是标明类型系统

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

class Ponit {x: number, y: number}
class Point2D {x: number, y: number}
const p: Point = new Point2D()

(1)Point和Point2D是两个同名称不同的类

(2)变量p的类型被显示标注为Point类型,但是它的值却是Point2D的实例,并且没有类型错误

(3)因为TS是结构化系统,只检查Point和Point2D的结构是否相同(相同,都具有x和y两个属性,属性类型也相同)

(4)但是,如果在标明类型系统中(比如C#,Java等),它们是不同的类,类型无法兼容

类型兼容性2:

在结构化系统中,如果两个对象具有相同的形状,则认为它们属于同一类型,这种说法是不正确的;

更准确的说法是:对于对象类型来说,y的成员至少与x相同,则x兼容y(成员多的可以赋值给成员少的)

class Point {x: number, y: number}
class Point3D {x: number, y: number}
const p: Point = new Point3D()

(1)Point3D的成员至少与Point相同,则Ponit兼容Point3D

(2)所以,成员多的Point3D可以赋值给成员少的Point

除了class之外,TS中其他类型也存在着相互兼容的情况(接口兼容,兼容函数等。。。)

接口之间的兼容类似于class:

interface Point {x: number, y: number}
interface Point2D {x: number, y: number}
let p1: Point
let p2:Ponit2D = p1
interface Point3D {x: number, y: number, z: number}
let p3: Point3D
// 多的可以赋值给少的
p2 = p3

并且,class和interface之间也可以兼容

interface Point3D {x: number, y: number, z: number}
let p3: Point2D = new Point3D()

函数之间的兼容性比较复杂:需要考虑:1、函数个数  2、参数类型  3、返回值类型

1、参数个数,参数多的兼容参数少的(或者说,少的可以赋值给多的)

type F1 = (a: number) => void
type F2 = (a: number, b: number) => void
let f1: F1
let f2: F2 = f1

(1)参数少的可以赋值给参数多的,所以f1可以赋值给f2

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

(3)并且因为回调函数是有类型的,所以TS会自动推导出item,index,array的类型

2、参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)

(1)原始类型:

type F1 = (a: number) => string
type F2 = (a: number) => string
let f1: F1
let f2: F2 = F1

说明:函数类型F2兼容函数类型F1,因为F1和F2的第一个参数类型相同

(2)对象类型:

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 = f2

说明:这个和前面写的那个接口兼容性冲突,将对象拆开,把每个属性看做一个个参数,则参数少的(f2)可以赋值给参数多的(f3)

(3)返回值类型:只关注返回值类型本身即可

// 原始类型
type F5 = () => string
type F6 = () => string
let f5: F5
let f6: F6 = f5
// 对象类型
type F7 = () => {name: string}
type F8 = () => {name: string, age: number}
let f7: F7
lei f8: F8
f7 = f8

说明:① 如果返回值是原始类型,此时两个类型要相同,比如 F5 和 F6

② 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的(比如F7和F8)

交叉类型:

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

比如:

interface Person {name: string}
interface Contact {phone: number}
type PersonDetail = Person & Contact
let obj: PersonDetail = {
    name: 'jack'
    phone: '133.....'
}

说明:使用交叉类型后,新的类型PersonDetail就同时具备了Person和Contact的所有属性类型;

相当于:

type PersonDetail = {
    name: string
    phone: string
}

交叉类型(&)和接口继承(extends)的对比:

相同点:都可以实现对象类型的组合

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

(1)继承(extends)

interface A {
    fn:(value: number) => string
}
// 这个B这会报错,(类型不兼容)
interface B extends A {
    fn:(value: number) => string
}

(2)交叉类型(&)

interface A {
    fn: (value: number) => string
}
interface B {
    fn: (value: number) => string
}
type C = A & B

说明:交叉类型没有错误,可以简单的理解为:

fn: (value: number | string) => string
let c: C
c.fn()  // 这个就相当于方法的重载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值