类 class
JS从ES6开始,引入了class关键字,可以更加方便的定义和使用类
TS作为JS的超集,支持使用class关键字,可以对类的属性和方法等进行静态类型检测
使用例子
class Person { name: string age: number constructor(name: string age: number){ this.name = name this.age = age }
running(){ console.log(this.name + ' running') } eating(){ console.log(this.name + ' eating') } }
const kun = new Person('ikun',18) kun.running()
|
类的继承
- 面向对象其中一大特征就为继承,可减小代码量
- 使用
extends
关键字来继承,子类使用 super
访问父类
class Student extends Person { id: number
constructor(name: string, age: number, id: number){ super(name,age) this.id = id }
running() { super.running() console.log(this.name + ' student running') } studying() { console.log(this.name + ' studying') } } const zs = new Student('zs',18,1)
zs.running()
zs.studying()
|
Student
可以有自己的属性和方法,并且会继承Person
的属性和方法
在构造函数中,我们可以通过super
来调用父类的构造方法,对父类中的属性进行初始化
类的成员修饰符
修饰符
在TS中类的属性和方法支持public
、private
、protected
三种修饰符
- public: 默认类型,公有属性和方法,任何地方可见
- private:私有类型,属性和方法仅同一类可见
- protected:保护类型,属性和方法仅同一类及子类可见
class Animal { private _name: string
constructor(_name: string){ this._name = _name } }
const cat = new Animal('kk')
console.log(cat._name)
|
只读属性readonly
如果属性我们不希望外界可以任意的修改,只希望确定值后直接使用,可以使用readonly
class Animal { readonly name: string constructor(name: string){ this.name = name } }
const cat = new Animal('kk')
cat.name = 'gg'
|
可选属性(Optional Properties)
interface IPerson { name: string age?: number }
const p: IPerson={ name: 'kun' }
|
getters/setters
因为被private修饰属性和方法的不能直接访问,使用存取器截取对对象成员的访问
class Person { private _name: string set name(newName){ this._name = newName } get name(newName){ return this._name }
constructor(name: string){ this._name = name } }
const p = new Person('kk') p.name = 'gg' console.log(p.name)
|
参数属性
TS 提供了特殊的语法,可以把一个构造函数参数转成一个同名同值的类属性
class Person { constructor(public name: string, private _age: number){} set age(newAge){ this._age = newAge } get age(){ return this._age } }
const kun = new Person('ikun',18) kun.age = 20 console.log(kun.age)
|
构造函数参数前添加一个可见性修饰符 public private protected 或者 readonly 来创建参数属性,最后这些类属性字段也会得到这些修饰符
抽象类abstract
继承是多态使用的前提,调用接口时传入父类,通过多态实现更加灵活的调用方法
所以父类不需要对某些方法进行具体的实现,可以定义抽象方法
抽象方法:
- 存在抽象类中
- 抽象类使用
abstract
声明的类
abstract class Shape { abstract getArea(): number }
class Circle extends Shape { constructor(private r: number ){ super() } getArea(){ return this.r * this.r * 3.14 } }
const circle = new Circle(3) console.log(circle.getArea())
|
特点
- 抽象类不能被实例化
- 抽象类除了抽象方法,还可以定义含有实现体的方法
- 抽象方法,一定是在抽象类中
- 抽象方法必须被子类实现
类的数据类型
类本身也可以当作数据类型
class Person { name: string age: number constructor(name: string age: number){ this.name = name this.age = age } }
const kun: Person = new Person('ikun',18) const gg: Person ={ name: 'kunkun' age: 11 }
|
索引签名(Index Signatures)
- 有的时候,你不能提前知道一个类型里的所有属性的名字,但是你知道这些值的特征
- 这种情况,你就可以用一个索引签名 (index signature) 来描述可能的值的类型
interface Icollertion{ [index:number]: any length: number }
function foo(collection: Icollertion){ for(let i = 0, i<= collection.length;i++){ console.log(collection[i]) } } const arr: string[] = ['aaa','bbb','ccc'] const tuple: [string,number,number] = ['ddd',1,3]
foo(arr)
foo(tuple)
|
一个索引签名的属性类型必须是 string
或者是 number
接口
继承
接口和类一样是可以进行继承的,也是使用extends关键字
不同的是接口可以实现多继承
interface IRun { running: ()=>void } interface ISwim { swimming: () => void }
interface Person extends IRun,ISwim{ name :string }
|
实现
使用 implements
来实现
与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约
interface IRun { name: string running: ()=>void }
class Person implements { name: string constructor(name: string){ this.name = name } running(){ console.log('running') } }
|
抽象类和接口的区别
抽象类在很大程度上和接口会有点类似:都可以在其中定义一个方法,让子类或实现类来实现对应的方法。
抽象类和接口么区别:
- 抽象类是事物的抽象,抽象类用来捕捉子类的通用特性,接口通常是一些行为的描述
- 抽象类通常用于一系列关系紧密的类之间,接口只是用来描述一个类应该具有什么行为
- 接口可以被多层实现,而抽象类只能单一继承
- 抽象类中可以有实现体,接口中只能有函数的声明
通常我们会这样来描述类和抽象类、接口之间的关系:
- 抽象类是对事物的抽象,表达的是
is a
的关系。猫是一种动物(动物就可以定义成一个抽象类) - 接口是对行为的抽象,表达的是
has a
的关系。猫拥有跑(可以定义一个单独的接口)、爬树(可以定义一个单独的接口)
的行为。
严格的字面量赋值检测

- 每个对象字面量最初都被认为是
新鲜的(fresh)
- 当一个新的对象字面量分配给一个变量或传递给一个非空目标类型的参数时,对象字面量指定目标类型中不存在的属性是错误的
- 当类型断言或对象字面量的类型扩大时,新鲜度会消失
简单理解,对象再第一次创建/实例化时,类型会严格检查,不符合会报错
对象已经存在再被赋值时,TS不进行类型检测了
interface Iinfo { name: string age: number }
const person1: Iinfo = { name: 'kunkun' }
const person2 = { name: 'kunkun' }
const person3: Iinfo = person2
|
枚举类型
- 枚举其实就是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型
- 枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型
关键字 enum
enum Position { LFET, RIGHT, TOP, BOTTOM }
function foo(position: Position){ switch(position){ case: position.LFET console.log('左边') break case: position.RIGHT console.log('右边') break } }
|
枚举类型默认是有值的
- 上面的
LFET
默认 为 0
、RIGHT
为 1
自增长 - 当我们不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的
- 如果自定义值 就如
LEFT = 1
,后面的RIGHT
为2
了 - 也可以赋值字符串,字符串枚举没有自增长的行为