一、类
// 使用class关键字来定义一个类
class Person {
// 定义实例属性, 通过实例进行访问person.name
name: string = 'jack'
// 在属性前使用 static 关键字可以定义类属性(静态属性)直接通过类名进行访问Person.age
static age: number = 18
// 以 readonly 开头的属性不可被修改
readonly gender: string = 'Male'
// 定义方法, 同样可以使用 static 关键字修饰
sayHi() {
console.log('Hi')
}
}
const person = new Person()
person.gender = "Female" // 报错: 无法分配到"gender", 因为它时一个只读属性
// 使用 abstract 关键字修饰的类表示抽象类,不能用来创建对象,是专门用来被继承的类
abstract class Animal {
name: string
constructor(name: string) {
this.name = name
}
/*
* 1. 通过 abstract 关键字修饰的方法为抽象方法,没有方法体
* 2. 抽象方法只能在抽象类中定义
* 3. 子类必须对父类中所定义的抽象方法进行重写
*/
abstract eat():void
}
// Cat类继承Animal类, 通过继承可以拥有父类的所有属性和方法
class Cat extends Animal {
age: number
constructor(name: string, age: number) {
// 如果在子类中写构造函数,在子类的构造函数中必须对父类构造函数进行调用
super(name) // 使用super函数对父类的构造函数进行调用
this.age = age
}
// 子类中添加同父类一样的方法,则子类方法会覆盖父类的方法
eat() {
// super.eat() 子类中同样可以使用super关键字调用父类的方法
console.log(2)
}
}
class Dog extends Animal {} //报错,没有对父类中的抽象方法进行重写
const animal = new Animal() //报错,无法创建抽象类的实例
const cat = new Cat('cat', 18)
console.log(cat) // {name: "cat", age: 18}
cat.eat() // 重写父类eat的方法,因此输出结果为 2
二、接口
/*
* 使用接口来定义一个类的结构,用来定义类中包含哪些属性和方法
* 同时接口也可以当作类型声明去使用
*/
interface customize {
name: string
age: number
}
interface customize {
gender: string
}
const obj: customize = {
name: 'jack',
age: 18
} // 报错,同名接口相当于是将里面定义的所有属性进行了合并,这里缺失了gender属性
const obj: customize = {
name: 'jack',
age: 18,
gender: '男' // 成功
}
/*
* 接口可以在定义类的时候去限制类的结构
* 接口所有的方法都是抽象方法
*/
interface customize {
name: string
hello() {} // 报错,接口中的所有属性不能有实际值,它只是定义了对象的结构,而不考虑实际值
}
interface customize {
name: string
hello(): void // 成功
}
/*
* 定义一个类时,可以使类去实现一个接口,实现接口就是使类满足接口的要求
* 接口和抽象类的不同点:
* 1. 抽象类中可以有抽象方法也可以有普通方法,但接口只能是抽象方法
* 2. 用抽象类的时候使用的是extends关键字,而定义一个接口的时候使用的是implements关键字
*/
class Person implements customize {
name: string
constructor (name: string) {
this.name = name
}
hello() {
console.log(`${this.name} say Hello`)
}
}
三、属性的封装
class Person {
/*
* 1.public修饰的属性可以在任意位置访问、修改 默认值
* 2.private 私有属性 只能在类内部进行访问、修改
* 3.protected 受保护的类,只能在当前类和当前类的子类中访问、修改
*/
private _name: string
private _age: number
constructor (name: string,age: number) {
this._name = name
this._age = age
}
// 定义方法来获取name属性
getName() {
return this._name //私有属性可以通过添加方法使私有属性能够被外部访问
}
// 定义方法来设置name属性
setName(value: string) {
this._name = value
}
// getter、setter简写形式
get age() {
console.log('age的值被读取!')
return this._age
}
set age(value: string) {
console.log('age的值被修改')
this._age = value
}
}
const person = new Person('jack', 19)
console.log(person._name) //报错 私有属性,只能在Person类中访问
person.setName('pink') //通过调用setName修改name属性值
console.log(person.getName()) // pink 通过调用getName获取私有属性值
person.age = 20
console.log(person.age) // 20
// 可以直接将属性定义在构造函数中
class Person {
constructor (public name: string, public age: number) {}
}
// 等同于
class Person {
name: string
age: number
constructor (name: string, age: number) {
this.name = name
this.age = age
}
}
四、泛型
// 在定义函数或者类时,遇到类型不明确的情况下可以使用泛型
function fn<T>(a: T): T { // 表示 a 的类型为T
return a
}
fn(10) // 不指定泛型,TS自动推断类型
fn<string>('jack') // 指定泛型
interface customize {
name: string
}
// T extends customize 表示泛型T必须是customize实现类
function fn1<T extends customize>(a: T): string{
return a.name
}
const result = fn1({name: 'jack'})
console.log(result) // jack
// 在类当中也可以使用泛型
class Person<T> {
// 定义了一个属性name,其类型为T
name: T
constructor(name: T) {
this.name = name
}
}
const person = new Person<string>('jack')