在 TypeScript 里,接口的作用就是为类型命名和为你的代码或第三方代码定义契约
一、接口初探
编译器只会检查那些必需的属性是否存在
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj); // 可以,但是{ size: 10, label: "Size 10 Object" }直接放进函数里面,作为参数会报错
二、可选属性
格式:在可选属性名字定义的后面加一个 ?
符号
有点:
1,可以对可能存在的属性进行预定义
2,可以捕获引用了不存在的属性时的错误
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
let newSquare = { color: "white", area: 100 };
if (config.clor) {
newSquare.color = config.clor; // 错误,SquareConfig接口没有clor这个属性
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({ color: "black" });
三、只读属性
定义:只能在对象刚刚创建的时候修改其值
// 定义
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // 错误
新增:ReadonlyArray 类型:它与 Array 相似,只是把所有可变方法去掉了
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // 错误
ro.push(5); // 错误
ro.length = 100; // 错误
a = ro; // 错误
a = ro as number[]; // 正确
ps):注意:readonly定义一个接口的属性,const定义一个变量
四、的属性检查
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig){
// ...
}
let mySquare = createSquare({ colour: "red", width: 100 }); // 直接作为参数会报错
// 绕开检查的方法(尽量不要用)
// 1,断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig); //
// 2,字符串索引签名
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any; // 任意的值
}
// 3,将这个对象赋值给一个另一个变量: 因为 squareOptions 不会经过额外属性检查,所以编译器不会报错。
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
五、函数类型
// 函数类型申明
interface SearchFunc {
(source: string, subString: string): boolean;
}
// 函数类型使用
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
};
//正确 参数和申明不一样也可以
mySearch = function(src: string, sub: string): boolean {
//
};
// 错误,返回值不能不匹配
mySearch = function(src, sub) {
let result = src.search(sub);
return "string";
};
六、可索引的类型
通过索引得到的类型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
Typescript 支持两种索引签名:字符串和数字。
同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number 来索引时,JavaScript 会将它转换成 string 然后再去索引对象。 也就是说用 100 (一个 number )去索引等同于使用 “100” (一个 string )去索引,因此两者需要保持一致。
// 1,数字索引的返回值必须是字符串索引返回值类型的子类型
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
interface NotOkay { // 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
[x: number]: Animal;
[x: string]: Dog;
}
// 2,定义的其他类型,必须是索引类型的子类型
interface NumberDictionary {
[index: string]: number;
length: number; // 可以,length是number类型
name: string; // 错误,`name`的类型与索引类型返回值的类型不匹配
}
interface NumberOrStringDictionary {
[index: string]: number | string;
length: number; // 可以,返回值属于索引类型返回值的子类
name: string; // 可以,返回值属于索引类型返回值的子类
}
// 3,只读索引
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // 错误
七、类类型
1,实现接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) {}
}
2,类静态部分与实例部分的区别
当一个类实现了一个接口时,只对其实例部分进行类型检查。 constructor 存在于类的静态部分,所以不在检查的范围内。
interface ClockConstructor { // 静态申明 和普通申明一样
new (hour: number, minute: number);
}
interface ClockInterface { // 实现接口 用 implements 关键字 , 只对其实例部分进行类型检查
tick();
}
const Clock: ClockConstructor = class Clock implements ClockInterface {
constructor(h: number, m: number) {}
tick() {
console.log("beep beep");
}
};
八、继承接口
能够从一个接口里复制成员到另一个接口里
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
// 一个接口的继承
interface Square1 extends Shape {
sideLength: number;
}
let square1 = {} as Square1;
square1.color = "blue";
square1.sideLength = 10;
// 多个接口的继承
interface Square2 extends Shape, PenStroke {
sideLength: number;
}
let square2 = {} as Square2;
square2.color = "blue";
square2.sideLength = 10;
square2.penWidth = 5.0;
九、混合类型
interface Counter {
(start: number): string; // 函数本身
interval: number; // 静态属性
reset(): void; // 静态方法
}
function getCounter(): Counter {
let counter = function(start: number) {} as Counter;
counter.interval = 123;
counter.reset = function() {};
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
十、接口继承类
当你创建了一个接口,继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)
在 Control 类内部,是允许通过 SelectableControl 的实例来访问私有成员 state 的
class Control {
private state: any;
}
interface SelectableControl extends Control { // 接口继承类
select(): void;
}
class Button extends Control implements SelectableControl {
select() {}
}
class TextBox extends Control {
select() {}
}
class ImageControl implements SelectableControl {
private state: any; // 错误继承
select() {}
}