接口
什么是接口 interface
https://www.tslang.cn/docs/handbook/interfaces.html
接口也是一种类型, 可以约束对象或者类的属性和方法, 只能有哪些属性或者必须有哪些属性
typescript
// 定义一个 PersonInterface 的接口, 被这个接口约束的变量,必须要有 name 属性和 age 属性,
// 并且, 这两个属性的类型必须要和接口中定义的一致
interface PersonInterface {
name: string;
age: number;
}
const p1 = {
name: "tom",
age: 10,
};
const p2 = {
name: "tom",
age: 10,
gender: "man", // 报错, 比接口多一个 gender 属性
};
const p3 = {
name: "tom", // 报错, 比接口少一个 age 属性
};
可选属性
默认情况下, 被接口限制的变量, 属性既不能多, 也不能少, 但是如果有可选属性
就能让约束的变量只实现一部分属性
typescript
interface FullNameInterface {
firstName: string;
lastName: string;
otherName?: string;
}
const n1: FullNameInterface = {
firstName: "zhang",
lastName: "shang",
};
索引签名
默认情况下, 被接口限制的变量, 属性既不能多, 也不能少, 但是如果用索引签名
就能让约束的变量多一部分自己独有的属性
typescript
interface FullName {
firstName: string;
lastName: string;
[key: string]: any; // 任意的 key 并且不限制 value 的类型
// [propName]: any; 老版本 ts 的写法是这样的
}
const names = {
firstName: "wang",
lastName: "Hai",
otherName: "XiaoWang", // 如果没有索引签名, 添加这个属性就会报错
};
只读属性
默认情况, 被约束的变量只要实现了接口属性就能随意读取和修改, 但是有些属性可能只需要 在定义的时候赋值一次
然后就不允许修改了, 此时就需要使用只读属性
typescript
interface FullNameInterface {
readonly firstName: string;
lastName: string;
}
let p1: FullNameInterface = {
firstName: "zhang",
lastName: "shang",
};
p1.firstName = "li"; // 报错: firstName 不能被修改
p1.lastName = "si";
函数接口
用来约束函数的参数个数类型及函数的返回值
typescript
// 实现这个接口的函数, 必须有2个参数, 并且类型必须是 number, 返回值的类型也必须是 number
interface sumInterface {
(x: number, y: number): number;
}
const add: sumInterface = function (x, y) {
return x + y;
};
嵌套结构
typescript
interface RouteConfig {
name: string;
path: string;
meta?: object;
children?: Array<RouteConfig>; // 引用自己, 必须是可选的, 否则死循环
}
// 使用 type 关键字也是可以的
// type RouteConfig = {
// name: string;
// path: string;
// meta?: object;
// children?: Array<RouteConfig>;
// }
const routes: RouteConfig[] = [
{
name: "login",
path: "/login",
meta: { noLogin: true },
},
{
name: "home",
path: "/",
children: [
{
name: "user_list",
path: "/users",
},
],
},
];
混合类型接口
https://www.tslang.cn/docs/handbook/interfaces.html
接口继承
- 接口继承是可以继承多个的
typescript
interface LengthInterface {
length: number;
}
interface WidthInterface {
width: number;
}
interface HeightInterface {
height: number;
}
interface RectInterface
extends LengthInterface,
WidthInterface,
HeightInterface {
color: string;
}
// 必须四个属性都有, 否则就报错
const rect: RectInterface = {
length: 10,
width: 20,
height: 30,
color: "red",
};
接口继承类
typescript
class Person {
public name;
public constructor(name: string) {
this.name = name;
}
public eat(): void {
console.info("人需要吃饭");
}
}
// PersonInterface接口会继承Person类的所有属性和方法
// 注意: 虽然会继承方法, 但是不会继承方法的实现
interface PersonInterface extends Person {
age: number;
say(): void;
}
class Student implements PersonInterface {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
eat() {
console.info("学生在学校吃饭");
}
say() {
console.info(`我的名字是:${this.name}, 我的年龄是:${this.age}`);
}
}
const s1 = new Student("张三", 15);
s1.say(); // 我的名字是:张三, 我的年龄是:15
s1.eat(); // 学生在学校吃饭
接口合并
定义两个同名的接口, 会自动合并, 如果去实现这个接口, 需要实现所有的属性和方法
typescript
interface PersonInterface {
gender: boolean;
}
interface PersonInterface {
age: number;
}
// 必须同时实现所有的定义
class Student implements PersonInterface {
gender: boolean = true;
age: number = 18;
}
关于接口的命名规范问题
- 以 I 为前缀:
ICreateUserArgs
- 以 Interface/contract 为后缀:
CreateUserArgsInterface
CreateUserArgsContract
类 class
修饰符
- public: 不设置权限,任意位置都可以访问,如果没有修饰符,默认 public
- protected: 本类及其继承本类的派生类可以访问
- private: 仅在本类内部可以访问, 在继承本类的派生类中不可覆盖
- readonly: 只读属性不允许修改(可以与其他 4 个权限修饰符共用)
- static: 静态属性, 这个属性属于类, 而不是对象(可以与其他 4 个权限修饰符共用)
typescript
class User {
private username: string;
private email: string;
private password: string;
constructor(username, email, password) {
this.username = username;
this.email = email;
this.password = password;
}
public getUsername() {
return this.username;
}
protected getEmail() {
return this.email;
}
// 只能在本类内部访问, 否则报错
private getPassword() {
return this.password;
}
}
class VIPUser extends User {
readonly isVIP: boolean = true;
static VIPLevels: Array<string> = ["会员", "超级会员", "永久会员"];
constructor(username, email, password) {
super(username, email, password);
}
public getUsername() {
// 在子类内部访问 public 修饰的方法
return "VIP " + this.getUsername();
}
public getEmail() {
// 在子类内部访问 protected 修饰的方法
return `<span style="color: #f00">${this.getEmail()}</span>`;
}
}
const tom = new User("tom", "tom@qq.com", "111111");
console.log(tom.getUsername());
// 在外部访问 public 修饰的方法
const jerry = new VIPUser("jerry", "jerry@qq.com", "222222");
// 在子类的外部访问 public 修饰的方法
jerry.isVIP = false; // 报错
console.log(VIPUser.VIPLevels);
继承 & 抽象类 & 实现接口
- 静态属性, 继承与 js ES6 中语法是一样的, 但是在 js 中默认是没有抽象类, 接口这些概念的, 也没有
interface
和implements
和abstract
关键字 - js 中默认没有抽象类, 可以简单理解为, 抽象类就是一个特殊的接口
- 实现接口:
interface
就是制定标准的关键字,implements
就是实现标准的关键字
typescript
interface IUser {
id: number;
username: string;
email: string;
password: string;
gender: number;
}
// 抽象类可以定义方法不实现, 也可以直接实现
// 不能直接用 new 关键字来创建抽象类的实例
abstract class Animal {
public eat(food: string): void {
console.log("动物需要吃食物");
}
public sleep(time: number): void;
}
// 子类如果继承抽象类, 必须要实现父类的方法
// 在 typescript 中, 实现 interface 可以使用多个
class User extends Animal implements IUser {
eat(food: string): void {}
sleep(time: number): void {}
}
使用静态属性实现单例
typescript
class Util {
private static instance: Util | null = null;
private consturctor() {
// 1. 使用 private 修饰 constructor
// 让这个类不能直接使用 new 来创建对象
}
static getInstance() {
// 2. 定义一个静态属性, 用于保存实例,
// 如果这个实例不为 null, 那就证明已经实例化过一次了,
// 直接返回这个实例
if (Util.instance === null) {
Util.instance = new Util();
}
return Util.instance;
}
}
const util1 = Util.getInstance();
const util2 = Util.getInstance();
console.log(util1 === util2); // true
type 和 interface 的区别
相同点
- 都可以用来约束(描述)对象和函数(参数, 返回值)
- 都可以扩展(interface:可以继承, type: 可以使用交叉类型)
typescript
// 可以描述对象或者方法
type CreateUserArgs = {
telphone_number: string;
email: string;
password: string;
gender: number;
};
type updateUser = (id: number, user: CreateUserArgs) => void;
interface ICreateUserArgs {
telphone_number: string;
email: string;
password: string;
gender: number;
}
interface IUpdateUser {
(id: number, user: ICreateUserArgs): void;
}
// 可以扩展
type UpdateUserArgs = CreateUserArgs & { id: number };
interface IUpdateUserArgs extends ICreateUserArgs {
id: number;
}
不同点
interface
可以但是type
做不到的
typescript
// 重名的接口自动合并
interface IUser {
id: number;
email: string;
}
interface IUser {
password: string;
gender?: number;
}
// 最终合并的接口为
interface IUser {
id: number;
email: string;
password: string;
gender?: number;
}
type
可以但是interface
做不到的
typescript
// 1. 描述非引用类型数据
type numstr = number | string;
let str = 11;
str = "hello";
// 2. 联合类型
type empty = null | undefined;