类
成员属性与方法定义
class User {
id: number;
username: string;
// 创建类的函数,当类通过new实例化的时候,就会执行该函数
constructor(id: number,username: string) {
console.log('这是构造函数,对类中成员属性进行初始化赋值');
this.id = id;
this.username = username;
}
postArticle(title: string) {
// 在类的内部可以通过 `this` 来访问成员属性和方法
console.log(`${this.username} 发表了一篇文章:${title}`)
}
}
let user = new User(1,'my')
user.postArticle('test')//my 发表了一篇文章:test
用public修饰符简化构造函数的赋值操作
class User {
// id: number;
// username: string;
//不用再声明属性和赋值
constructor(public id: number,public username: string) {
// this.id = id;
// this.username = username;
}
postArticle(title: string) {
console.log(`${this.username} 发表了一篇文章:${title}`)
}
}
let user = new User(1, 'my')
user.postArticle('test')//my 发表了一篇文章:test
继承extends
constructor
如果子类没有重写构造函数constructor,则会在默认的 constructor 中调用 super()
如果子类有自己的构造函数,则需要在子类构造函数中显示的调用父类构造函数 : super()
super
在子类构造函数中只有在 super() 之后才能访问 this
在子类中,可以通过 super 来访问父类的成员属性和方法
通过 super 访问父类的的同时,会自动绑定上下文对象为当前子类 this
class VIP extends User {
constructor(id: number, username: string, public score = 0) {
super(id, username);
}
postAttachment(file: string): void {
//可以用super调用父类的方法,自动绑定上下文对象为当前子类 this
super.postArticle(title);//Leo 发表了一篇文章:1.png
console.log(`${this.username} 上传了一个附件: ${file}`)
}
}
let vip1 = new VIP(1, 'Leo');
vip1.postArticle('标题');
vip1.postAttachment('1.png');
方法的重写与重载
子类成员方法继承自父类,但是子类也可以对它们进行重写和重载
重写
class VIP extends User {
constructor(id: number, username: string, public score: number = 0) {
super(id, username);
}
//重新实现了父类的方法:参数的个数和参数的类型一致
postArticle(title: string) {
//重写方法内容
this.score++;
console.log(`${this.username} 发表了一篇文章: ${title},积分:${this.score}`);
}
}
let vip1 = new VIP(1, 'Leo');
vip1.postArticle('标题');
重载
class VIP extends User {
constructor(id: number, username: string, public score: number = 0) {
super(id, username);
}
// 参数个数,参数类型不同:重载
postArticle(title: string): void;
postArticle(title: string, file: string): void;
postArticle(title: string, file?: string) {
if (file) {
console.log(file)
} else {
super.postArticle(title);
}
}
}
let vip1 = new VIP(1, 'Leo');
vip1.postArticle('标题');
vip1.postArticle('标题', 'file');
修饰符
限制不同的访问级别
public修饰符
自身、子类、类外
protected饰符
自身。子类
private修饰符
自身
readonly 修饰符
只读修饰符只能针对成员属性使用,且必须在声明时或构造函数里被初始化
自身、子类、类外
寄存器
除了修饰符也可以用getter和setter来实现对属性的控制
class User {
constructor(readonly _id: number, readonly _username: string, private _password: string) {
}
public set password(password: string) {
if (password.length >= 6) {
this._password = password;
}
}
public get password() {
return '******';
}
}
let user1 = new User(1, 'mt', '123456');
console.log(user1.password);//******
静态成员
静态成员是属于类的,而不是实例后的对象。使用 类名.静态成员 来访问
type IAllowFileTypeList = 'png' | 'gif' | 'jpg' | 'jpeg' | 'webp';
class User {
static readonly ALLOW_FILE_TYPE_LIST: Array<IAllowFileTypeList> = ['png', 'gif', 'jpg', 'jpeg', 'webp'];
constructor(
public id: number,
public username: string,
private _allowFileTypes: Array<IAllowFileTypeList>
) { }
static info(): void {
console.log(User.ALLOW_FILE_TYPE_LIST);//User类允许的文件类型
}
info2(): void {
console.log(this._allowFileTypes);//实例后的对象允许的文件类型
}
}
let user1 = new User(1, 'mt', ['png', 'gif']);
console.log(User.ALLOW_FILE_TYPE_LIST);//[ 'png', 'gif', 'jpg', 'jpeg', 'webp' ]
User.info();//[ 'png', 'gif', 'jpg', 'jpeg', 'webp' ]
user1.info2();//[ 'png', 'gif' ]
抽象类abstract
有时候,一个基类(父类)的一些方法无法确定具体的行为,而是由继承的子类去实现。
使用抽象类可以约定所有继承子类的所必须实现的方法,使类的设计更加的规范。
注意事项:
abstract 修饰的方法不能有方法体
如果一个类有抽象方法,那么该类也必须为抽象的
如果一个类是抽象的,那么就不能使用 new 进行实例化(因为抽象类表名该类有未实现的方法,所以不允许实例化)
如果一个子类继承了一个抽象类,那么该子类就必须实现抽象类中的所有抽象方法,否则该类还得声明为抽象的
例子:React组件设计
abstract class Component<T1, T2> {
props: T1;
state: T2;
constructor(props: T1) {
this.props = props;
}
abstract render(): string;
}
类与接口 implements
当一个抽象类中只有抽象的时候,这个时候,更推荐通过接口的方式来定义契约。
抽象类编译后还是会产生实体代码,而接口不会
TypeScript 只支持单继,但是一个类可以实现多个接口,多个接口使用 , 分隔
implements 与 extends 可同时存在
interface ILog {
getInfo(): string;
}
interface IMyComponentProps {
val: number;
}
interface IMyComponentState {
x: number;
}
class MyComponent extends Component<IMyComponentProps, IMyComponentState> implements ILog {
constructor(props: IMyComponentProps) {
super(props);
this.state = {
x: 1
}
}
render() {
return '123';
}
getInfo() {
return `组件:MyComponent,props:${this.props},state:${this.state}`;
}
}
多个接口用,号分割
interface ILog {
getInfo(): string;
}
interface IStorage {
save(data: string): void;
}
class MyComponent extends Component<IMyComponentProps, IMyComponentState> implements ILog, IStorage {
//...
}
接口也可以继承
interface ILog {
getInfo(): string;
}
interface IStorage extends ILog {
save(data: string): void;
}
类与对象类型
当我们在 TypeScript 定义一个类的时候,其实同时定义了两个不同的类型
类类型(构造函数类型,js的类本质是个函数)
对象类型(实例后的对象)
class Person {
// 属于类的
static type = '人';
// 属于实例的
name: string;
age: number;
gender: string;
// 类的构造函数也是属于类的
constructor( name: string, age: number, gender: '男'|'女' = '男' ) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 属于实例的
public eat(): void {
// ...
}
}
对应于以下两种接口
interface Person {
name: string,
age: number,
gender: string,
eat(): void
}
interface PersonConstructor {
//构造函数的标注前面要加个new
new (name: string, age: number, gender: '男'|'女'): Person,
type: string
}
使用时的类型标注区别如下
// 接受实例类型
function fn1(arg: Person) {
arg.eat();
}
fn1(new Person('zMouse', 35, '男'));
// 接受类/构造函数
function fn1(arg: typeof Person) {
new arg('zMouse', 35, '男');
}
fn1(Person);