在 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() {}}
