Skip to content

类和接口

下面是使用 TS 约束属性并实例化对象

typescript
class User {
    name: string
    age: number
    constructor(n: string, a: number) {
        this.name = n
        this.age = a;
    }

    info(): string {
        return `${this.name}的年龄是 ${this.age}`
    }
}

const hd = new User('后盾人', 12);
const xj = new User('向军', 18)

通过约束数组的类型为User,使其成员只能是 User 类型对象

typescript
...
const users: User[] = [hd, xj];
console.log(users);
...

修饰符

public

下面来介绍第一个访问修饰符 public,指公开的属性或方法

  • 默认情况下属性是 public (公开的),即可以在类的内部与外部修改和访问
  • 不明确设置修饰符即为 public
typescript
class User {
    public name: string
    public age: number
    constructor(n: string, a: number) {
        this.name = n
        this.age = a;
    }

    public info(): string {
        return `${this.name}的年龄是 ${this.age}`
    }
}

const hd = new User('后盾人', 12);
const xj = new User('向军', 18)

hd.name = "后盾人网站"

for (const key in hd) {
    if (hd.hasOwnProperty(key)) {
        console.log(key);
    }
}

protected

protected 修饰符指受保护的,只允许在父类与子类使用,不允许在类的外部使用

typescript
class Hd {
    protected name: string
    constructor(name: string) {
        this.name = name
    }
}
class User extends Hd {
    constructor(name: string) {
        super(name)
    }

    public info(): string {
        return `你好 ${this.name}`
    }
}

const hd = new User('后盾人');

console.log(hd.info());
console.log(hd.name); //属性是 protected 不允许访问

private

private 修饰符指私有的,不允许在子类与类的外部使用

父类声明 private 属性或方法子类不允许覆盖

typescript
class Hd {
    private name: string
    constructor(name: string) {
        this.name = name
    }
    private info(): void { }
}
class User extends Hd {
    constructor(name: string) {
        super(name)
    }

    public info(): void {

    }
}

子类不能访问父类的 private 属性或方法

typescript
class Hd {
    private name: string
    constructor(name: string) {
        this.name = name
    }
}
class User extends Hd {
    constructor(name: string) {
        super(name)
    }

    public info(): string {
        return `你好 ${this.name}` //属性是 private 不允许子类访问
    }
}

子类更改父类方法或属性的访问修饰符有些限制的

  • 父类的 private 不允许子类修改
  • 父类的 protected 子类可以修改为 protected 或 public
  • 父类的 public 子类只能设置为 public

readonly

readonly 将属性定义为只读,不允许在类的内部与外部进行修改

typescript
class Axios {
    readonly site: string = 'https://houdunren.com/api'
    constructor(site?: string) {
        this.site = site || this.site
    }
    public get(url: string): any[] {
        console.log(`你正在请求 ${this.site + '/' + url}`)
        return []
    }
}

constructor

构造函数是初始化实例参数使用的,在 TS 中有些细节与其他程序不同

我们可以在构造函数 constructor 中定义属性,这样就不用在类中声明属性了,可以简化代码量

  • 必须要在属性前加上 public、private、readonly 等修饰符才有效

    typescript
    class User {
        constructor(
            public name: string,
            public age: number
        ) {}
    
        public info(): string {
            return `${this.name}的年龄是 ${this.age}`
        }
    }

static

static 用于定义静态属性或方法,属性或方法是属于构造函数的

  • 静态属性是属于构造函数的,不是对象独有的,所以是所有对象都可以共享的
语法介绍

下面是 static 使用的语法

typescript
class Site {
    static url: string = 'houdunren.com'

    static getSiteInfo() {
        return '我们不断更新视频教程在' + Site.url
    }
}

单例模式

当把 construct 定义为非 protected 修饰符后,就不能通过这个类实例化对象了。

typescript
class User {
    protected constructor() {

    }
}

const hd = new User();

我们可以利用这个特性再结合 static 即可实现单例模式,即只实例化一个对象

typescript
class User {
    static instance: User | null = null;
    protected constructor() { }

    public static make(): User {
        if (User.instance == null) User.instance = new User;

        return User.instance;
    }
}

const hd = User.make();

Getter / setter

typescript
class User {
    private _name
    constructor(name: string) {
        this._name = name
    }
    public get name() {
        return this._name;
    }
    public set name(value) {
        this._name = value
    }
}

const hd = new User('向军')
hd.name = '李四'
console.log(hd.name);

因为 get 与 set 是新特性所以编译时要指定 ES 版本

tsc 1.ts -w -t es5

abstract

抽象类定义使用 abstract 关键字,抽象类除了具有普通类的功能外,还可以定义抽象方法

  • 抽象类可以不包含抽象方法,但抽象方法必须存在于抽象类中
  • 抽象方法是对方法的定义,子类必须实现这个方法
  • 抽象类不可以直接使用,只能被继承
  • 抽象类类似于类的模板,实现规范的代码定义
typescript
class Animation {
    protected getPos() {
        return { x: 100, y: 300 }
    }
}

class Tank extends Animation {
    public move(): void {

    }
}

class Player extends Animation {
    public move: void{

}

上例中的子类都有 move 方法,我们可以在抽象方法中对其进行规范定义

  • 抽象方法只能定义,不能实现,即没有函数体
  • 子类必须实现抽象方法
typescript
abstract class Animation {
    abstract move(): void
    protected getPos() {
        return { x: 100, y: 300 }
    }
}

class Tank extends Animation {
    public move(): void {

    }
}

class Player extends Animation {
    public move(): void {

    }
}

子类必须实现抽象类定义的抽象属性

typescript
abstract class Animation {
    abstract move(): void
    abstract name: string
    protected getPos() {
        return { x: 100, y: 300 }
    }
}
class Tank extends Animation {
    name: string = '坦克'
    public move(): void {

    }
}

class Player extends Animation {
    name: string = '玩家'

    public move(): void {

    }
}

抽象类不能被直接使用,只能被继承

typescript
abstract class Animation {
    abstract move(): void
    protected getPos() {
        return { x: 100, y: 300 }
    }
}
const hd = new Animation(); //报错,不能通过抽象方法创建实例

Interface

接口用于描述类和对象的结构

  • 使项目中不同文件使用的对象保持统一的规范
  • 使用接口也会支有规范更好的代码提示

抽象类

typescript
interface AnimationInterface {
    name: string
    move(): void
}
abstract class Animation {
    protected getPos(): { x: number; y: number } {
        return { x: 100, y: 300 }
    }
}

class Tank extends Animation implements AnimationInterface {
    name: string = '敌方坦克'
    public move(): void {
        console.log(`${this.name}移动`)
    }
}

对象

下面使用接口来约束对象

typescript
interface UserInterface {
    name: string;
    age: number;
    isLock: boolean;
    info(other:string): string,
}

const hd: UserInterface = {
    name: '后盾人',
    age: 18,
    isLock: false,
    info(o:string) {
        return `${this.name}已经${this.age}岁了,${o}`
    },
}

如果尝试添加一个接口中不存在的函数将报错,移除接口的属性也将报错。

如果有额外的属性,使用以下方式声明,这样就可以添加任意属性了。

typescript
interface UserInterface {
    name: string;
    age: number;
    isLock: boolean;
    [key:string]:any
}

接口继承

下面定义游戏结束的接口 PlayEndInterface ,AnimationInterface 接口可以使用 extends 来继承该接口

typescript
interface PlayEndInterface {
    end(): void
}
interface AnimationInterface extends PlayEndInterface {
    name: string
    move(): void
}

对象(class)可以使用实现多个接口,多个接口用逗号连接

typescript
interface PlayEndInterface {
    end(): void
}
interface AnimationInterface {
    name: string
    move(): void
}

class Animation {
    protected getPos(): { x: number; y: number } {
        return { x: 100, y: 300 }
    }
}

class Tank extends Animation implements AnimationInterface, PlayEndInterface {
    name: string = '敌方坦克'
    public move(): void {
        console.log(`${this.name}移动`)
    }
    end() {
        console.log('游戏结束');
    }
}

函数

下面使用 UserInterface 接口约束函数的参数与返回值

  • 会根据接口规范提示代码提示
  • 严格约束参数类型,维护代码安全

函数参数

下面是对函数参数的类型约束

typescript
interface UserInterface {
    name: string;
    age: number;
    isLock: boolean;
}

function lockUser(user: UserInterface, state: boolean): UserInterface {
    user.isLock = state;
    return user;
}

函数声明

使用接口可以约束函数的定义

typescript
interface Pay {
    (price: number): boolean
}
const getUserInfo: Pay = (price: number)=>true

构造函数

下面的代码我们发现需要在多个地方使用对 user 类型的定义

typescript
class User {
    info: { name: string, age: number }
    constructor(user: { name: string, age: number }) {
        this.info = user
    }
}
const hd = new User({ name: '后盾人', age: 18 })
console.log(hd);

使用 interface 可以优化代码,同时也具有良好的代码提示

typescript
interface UserInterface {
    name: string,
    age: number
}
class User {
    info: UserInterface
    constructor(user: UserInterface) {
        this.info = user
    }
}
const hd = new User({ name: '后盾人', age: 18 })
console.log(hd);

数组

对数组类型使用接口进行约束

typescript
const hd: UserInterface = {
    name: '后盾人',
    age: 18,
    isLock: false
}

const xj: UserInterface = {
    name: '向军',
    age: 16,
    isLock: false
}

const users: UserInterface[] = [];
users.push(hd, xj)
console.log(users);

枚举

typescript
enum SexType {
    BOY, GIRL
}

interface UserInterface {
    name: string,
    sex: SexType
}

const hd: UserInterface = {
    name: '后盾人',
    sex: SexType.GIRL
}
console.log(hd); //{ name: '后盾人', sex: 1 }

type

ype 与 interface 非常相似都可以描述一个对象或者函数,使用 type 用于定义类型的别名,是非常灵活的类型定义方式。

  • type 可以定义基本类型别名如联合类型,元组
  • type 与 interface 都是可以进行扩展
  • 使用 type 相比 interface 更灵活
  • 如果你熟悉其他编程语言,使用 interface 会让你更亲切
  • 使用类(class) 时建议使用接口,这可以与其他编程语言保持统一
  • 决定使用哪个方式声明类型,最终还是看公司团队的规范
基本使用

下面是使用 type 声明对象类型

typescript
type User = {
    name: string,
    age: number
}
const hd: User = { name: '后盾人', age: 18 }

上面已经讲解了使用 interface 声明函数,下面来看使用 type 声明函数的方式

typescript
type Pay = (price: number) => boolean
const wepay: Pay = (price: number) => {
    console.log(`微信支付${price}`);
    return true;
}

wepay(100)
类型别名

type 可以为 number、string、boolean、object 等基本类型定义别名,比如下例的 IsAdmin。

typescript
//基本类型别名
type IsAdmin = boolean

//定义联合类型
type Sex = 'boy' | 'girl'

type User = {
    isAdmin: IsAdmin,
    sex: Sex
}
const hd: User = {
    isAdmin: true,
    sex: "boy"
}

//声明元组
const users: [User] = [hd]
索引类型

type 与 interface 在索引类型上的声明是相同的

typescript
interface User {
    [key: string]: any
}

type UserTYpe = {
    [key: string]: any
}
声明继承

typescript 会将同名接口声明进行合并

typescript
interface User {
    name: string
}
interface User {
    age: number
}
const hd: User = {
    name: '后盾人',
    age: 18
}

interface 也可以使用 extends 继承

typescript
interface Admin {
    role: string
}
interface User extends Admin {
    name: string
}
const hd: User = {
    role: 'admin',
    name: '后盾人',
}

interface 也可以 extends 继承 type

typescript
type Admin = {
    role: string
}
interface User extends Admin {
    name: string
}
const hd: User = {
    role: 'admin',
    name: '后盾人',
}

type 与 interface 不同,存在同名的 type 时将是不允许的

typescript
type User {
    name: string
}
type User {
    age: number
}

不过可以使用& 来进行 interface 的合并

typescript
interface Name {
    name: string
}
interface Age {
    age: number
}
type User = Name & Age

下面是 type 类型的声明合并

typescript
type Admin = {
    role: string,
    isSuperAdmin: boolean
}
type Member = {
    name: string
}

type User = Admin & Member;

const hd: User = {
    isSuperAdmin: true,
    role: 'admin',
    name: '后盾人'
}
implements
typescript
type Member = {
    name: string
}

class User implements Member {
    name: string = '后盾人'
}