TS 基础学习(三)
class
TypeScript全面支持ES2015中引入的class关键字,并为其添加了类型注解和其他语法(比如,可见修饰符等)
class Person {
name: string = 'a'
}
const p = new Person()
console.log(p); // Person { name: 'a' }
1、根据TS中的类型推论,可以知道Person类的实例对象p的类型是Person。
2、TS中的class,不仅仅提供了class的语法功能,也作为一种类型存在
实例属性初始化
class Person {
name = 'a'
age: number
}
1、声明成员age,类型为number(没有初始值)
2、声明成员name,并设值初始值,此时,可省略类型注解(TS会自动推论为string类型)
class类中的构造函数
class Person1 {
age: number
gender: string
constructor(age: number, gender: string) {
this.age = age
this.gender = gender
}
}
1、成员初始化(比如age:number)后,才可以通过this.age来访问实例成员
2、需要为构造函数指定类型注解,否则会被隐式推断为any;构造函数不需要返回值类型
class中的实例方法
class Point {
x = 10
y = 10
scale(n: number): void {
this.x *= n
this.y *= n
}
}
1、方法的类型注解(参数和返回值)与函数用法相同
class 类的继承
类继承的两种方式: 1.extends(继承父类); 2 implements(实现接口)
// 定义一个接口
interface Singable {
sing(): void
}
class Person implements Singable {
sing(): void {
console.log('实现接口的方法');
}
}
const p = new Person()
p.sing()
1、通过implements关键字让class实现接口
2、Person类实现接口Singable意味着,Person类中必须提供Singable接口中指定的所有方法和属性
class 类成员可见性修饰符
类成员可见性: 可以使用TS来控制class的方法或属性对于class外的代码是否可见
可见性修饰符包括:public(公有的)、protected(受保护的)、private(私有的)
public: 表示公有的、公开的,公有成员可以被任何地方访问,默认public
class Animal {
public move() {}
}
1、在类属性或方法前面添加public关键字,来修饰该属性或方法是公有的。
2、因为public是默认可见性,所以,可以直接省略。
protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见
class Animal1 {
protected move() {}
}
class Dog extends Animal1 {
bark() {
console.log('1111');
this.move()
}
1、在类属性或方法前面添加protected关键字,来修饰该属性或方法是受保护的
2、在子类的方法内部可以通过this来访问父类中受保护的成员,但是,对实例不可见
private: 表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。
class Animal2 {
private move() {}
walk() {
this.move()
}
}
1、在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的。
2、私有的属性或方法只在当前类中可见,对子类和实例对象也都是不可见的
readonly 只读修饰符
readonly:表示只读,用来防止在构造函数之外对属性进行赋值。
class Person1 {
// 只要是readonly修饰的属性,必须手动提供明确的类型
readonly age: number = 18
constructor(age: number) {
this.age = age
}
}
1、使用readonly关键字修饰该属性是只读的,注意只能修饰属性不能修饰方法。
2、注意: 属性age后面的类型注解(比如,此处的number)如果不加,则age的类型为18(字面量类型)
3、接口或则{} 表示的对象类型,也可以使用readonly。
类型兼容性
TS采用的是结构化类型系统, 也叫做duck typing(鸭子类型), 类型检查关注的是值所具有的形状, 也就是说,在结构 类型系统重, 如果两个对象具有相同的形状,则认为他们属于同一类型。
class Point { x: number; y: number }
class Point2D { x: number; y:number }
const p: Point = new Point2D()
Point和Point2D是两个名称不同的类
1、变量p的类型显示标注为Point类型,但是,它的值确实Point2D的实例, 并且没有类型错误
2、因为TS是结构化类型系统,只检查Point和Point2D的结构是否相同(想通,都具有x和y两个属性,属性类型也想通)
3、但是,如果在Nominal Type System中(比如,C#、Java等),他们是不同的类,类型无法兼容。
注意: 在结构化类型系统重,如果两个对象都具有相同的形状,则认为他们属于同一类型,这种说法并不准确
更准确的说法:对于对象类型来说, y的成员至少与x相同, 则x兼容y(成员多的可以赋值给少的)
class Point { x: number; y: number }
class Point3D { x: number; y:number; z: number }
const p: Point = new Point3D()
1、Point3D的成员至少与Point相同, 则Point兼容Point3D
2、所以,成员多的Point3D可以赋值给成员少的Point
接口(interface)兼容性
除了class 之外,TS中的其他类型也存在相互兼容的情况,包括:接口兼容性、 函数兼容性等
接口之间的兼容性,类似于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 = { x: 10, y: 20}
let p2: Point2D = p1
let p3: Point3D = { x: 10, y: 20, z: 30}
p2 = p3
// 定义一个类Point4D 和 Point3D的结构完全一致
class Point4D {
x: number; y: number; z: number
}
// 声明的类型是接口, 但是可以把类的实力对象复制给p4
let p4: Point2D = new Point4D()
console.log(p4);// Point4D { x: 10, y: 20, z: 30 }
函数兼容性
函数之间兼容性比较复杂需要考虑:1.参数个数、2.参数类型、3.返回值类型
参数个数,参数多的兼容参数少的(或则说,参数少的可以赋值给参数多的)
1、参数少的可以赋值给参数多的,所以f1可以赋值给f2
2、数组forEach方法第一个参数是回调函数, 该示例中类型为:(value: string, index: number, array:string[]) => void
3、在JS中省略用不到的函数参数实际上是很常见的,这样的使用方式,促使了TS中函数类型之间的兼容性
4、并且因为回调函数是有类型的,所以,TS会自动推倒出参数item、index、array的类型
参数类型,想同位置的参数类型要相同(原始类型)或兼容(对象类型)
type F1 = (a: number) => void
type F2 = (b: number) => void
let f1: F1 = (a) => { }
let f2: F2 = f1
函数类型F2兼容函数类型F1,因为F1和F2的第一个参数类型相同
注意: 此处与前面讲的接口兼容性冲突
技巧:将对象拆开,把每个属性看做一个个参数,则,参数少的(f3)可以赋值给参数多的(f4)
返回值类型,只关注返回值类型本身即可
1、如果返回值类型是原始类型,此时两个类型要相同,比如f5和f6
2、如果返回值类型是对象类型, 此时成员多的可以赋值给成员少的,比如f7和f8