0%

TypeScript接口

接口

接口可以约束对象,函数,类的结构和类型,这是一种代码协作的契约,必须遵守,不能改变。

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

  • 接口关键字:interface
  • 类型断言:用as 或者<>的形式 ,后者在react中使用会出问题
  • 可选属性:通过?来设置
  • 只读属性:通过readonly 来设置
  • 当不确定接口中属性个数时需要使用 索引签名
  • 索引签名包括字符串索引签名数字索引签名
  • 当接口中定义了一个索引后,例如设置了 【x:string】= string,就不能设置y:number了。因为设置了【x:string】= string相当于这个接口的字符串索引返回值都是字符串,而y:number违背这一原则,冲突了
  • 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。因为 数字索引或转化为字符串索引,而转化的这部分索引对应的值的类型范围 超过了 字符串索引类型的范围,就会报错,相当于超出范围。

readonly vs const

做为变量使用的话用 const,若做为属性则使用 readonly。

对象类型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface SquareConfig {
color?: string; // 可选属性
width?: number; // 可选属性
}

function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = { color: "white", area: 100 };
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}

// 类型“{ colour: string; width: number; }”的参数不能赋给类型“SquareConfig”的参数。
// 对象文字只能指定已知的属性,但“colour”中不存在类型“SquareConfig”。是否要写入 color?
let mySquare = createSquare({ colour: "red", width: 100 });

对象字面量会被特殊对待而且会经过额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。

绕过这种检测的方式有三种:

  • 将对象字面量赋值给一个变量,直接操作变量
  • 使用类型断言
  • 使用索引签名
1
2
3
4
5
6
7
8
9
10
11
12
13
// 将对象字面量赋值给一个变量
let config = { colour: "red", width: 100 };
let mySquare = createSquare(config);

// 使用类型断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

// 使用索引签名
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any
}

函数类型接口

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用变量定义函数类型
let add1: (x: number, y: number) => number

// 使用类型别名定义函数类型
type add2 = (x: number, y: number) => number

// 使用函数类型接口定义函数类型
interface add3 {
(x: number, y: number): number
}

// TypeScript 的类型系统会推断出参数类型,函数的返回值类型是通过其返回值推断出来的
let add: add3 = (x, y) => x + y;

类类型接口

与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。

  • 类实现接口时,必须实现接口中所有属性和方法
  • 类可以扩展新的属性和方法
  • 接口只能约束类的公有成员
  • 接口不能约束类的构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Human {
// 类“Asian”错误实现接口“Human”。类型“Asian”提供的内容与签名“new (name: string): void”不匹配。
// new(name: string): void
name: string;
eat(): void;
}

class Asian implements Human {
constructor(name: string) {
this.name = name;
}
name: string
// 类“Asian”错误实现接口“Human”。属性“eat”在类型“Asian”中是私有属性,但在类型“Human”中不是。
// private eat() {}
eat() {}
age: number = 0
sleep() {}
}

继承接口

和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。

1
2
3
4
5
6
7
8
9
10
11
interface Shape {
color: string
}

interface Square extends Shape {
sideLength: number
}

let square = {} as Square
square.color = 'blue'
square.sideLength = 10

一个接口可以继承多个接口,创建出多个接口的合成接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Shape {
color: string
}

interface PenStroke {
penWidth: number
}

interface Square extends Shape, PenStroke {
sideLength: number
}

let square = {} as Square
square.color = 'blue'
square.sideLength = 10
square.penWidth = 5.0

混合类型接口

一个接口既可以定义一个函数也可以像对象一样拥有属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义一个类库接口 
// lib是一个函数,并且拥有version属性,doSomeThing方法
interface Lib{
(): void;
version: string;
doSomeThing(): void;
}

function getLib() {
let lib: Lib = (() => {}) as Lib; // 类型断言
lib.version = "1.0";
lib.doSomeThing = () => {};
return lib;
}

接口继承类

接口把类的成员都进行了抽象,也就是只有类的结果,而没有具体实现。

当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的 private 和 protected 成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。

注意,这么做的目的是限定接口的使用范围,并不会真正为这个接口添加类的私有和受保护属性(实际上接口也没有这种类型的属性),而这个限定范围就是:只能由子类来实现这个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Parent {
state = 1
private state2 = 0
protected state3 = 3
}
interface ChildInterface extends Parent {}
class Child1 extends Parent implements ChildInterface {
showMsg() {
// console.log(this.state2); // 属性“state2”为私有属性,只能在类“Parent”中访问。
console.log(this.state3) // 可以访问来自接口的被保护属性
}
}

// 类“Child2”错误实现接口“ChildInterface”。Type 'Child2' is missing the following properties from type 'ChildInterface': state, state2, state3
class Child2 implements ChildInterface {

}

// 实例对象可以访问父类的私有或受保护的成员
let bus = new Child1()
bus.showMsg()
// Bus {state: 1, state2: 0, state3: 3}
console.log(bus)

参考

-------------本文结束感谢您的阅读-------------