TS 核心原则之一就是对值所具有的结构进行类型检查。在 TS 里,接口的作用就是为这些类型命名,为代码或第三方代码定义契约。
- 接口初探
- 可选属性
- 只读属性
- 额外属性检查
- 函数类型
- 可索引的类型
- 类类型
- 继承接口
- 混合类型
- 接口继承类
1. 接口初探
interface LabeledValue {
label: string;
}
2. 可选属性
interface SquareConfig {
color?: string;
width?: number;
}
3. 只读属性
只读属性创建后不能再次修改。
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
readonly vs const:对象作为变量时使用 const,作为属性时使用 readonly
4. 额外属性检查
可以漏写部分属性,但不能多写属性或者错写属性,即如果传入的对象中含有目标类型不存在的属性,TS 额外的属性检查机制会进行报错。
官方提供了绕开 TS 的额外属性检查的办法:
// 1. 使用类型断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
// 2. 声明接口时来一个任意类型,此时这个声明为该接口的对象,可以有任意数量、任意类型、只要名字不是 color 或 width 的属性
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
// 3. 更简单,只要把对象赋值到另一个变量上(抽离出来)即可:
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
需要注意的是,只在某些复杂场景下才去考虑绕过额外属性检查,因为通常额外属性检查是用来发现潜在 bug 的。
5. 函数类型
接口能够用来描述一个 JavaScript 对象所能具有的各种样子(属性),另外也能用来描述函数的类型,其实就是接口中描述函数的签名。
interface SearchFunc {
(source: string, subString: string): boolean;
}
名字不必一一对应,只要顺序对应上即可:
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
return result > -1;
}
不写函数的类型也可以,TS 会自动类型推断:
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
return result > -1;
}
6. 可索引的类型
就像使用接口描述函数类型,我们也可以在接口中描述能够“通过索引得到”的类型,比如a[10]
或 fullName["lastName"]
。可索引类型有一个索引签名,描述了对象的索引类型和返回值类型。
TS 中的索引签名也有两种:number 和 string。
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
接口中继续定义其他属性和类型的时候,要和索引签名所匹配:
interface NumberDictionary {
[index: string]: number;
length: number; // 可以,length是number类型
name: string // 错误,`name`的类型与索引类型返回值的类型不匹配
}
7. 类类型(Class Types)
类类型总算和 Java 里接口的基本作用一样了,在接口中定义的属性和方法,当一个类说自己符合某种接口契约时,该类就要至少实现这个接口中定义了的属性和方法,才能说自己是某种接口定义的类型:
interface ClockTnterface {
currentTime: Date; // 定义属性成员
setTime(d: Date); // 定义方法成员
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
类的类型分为:实例部分的类型和静态部分的类型,定义一个类时不能去直接实现(implements)一个构造器接口,因为当一个类实现了一个接口时,只对接口的实例部分进行类型检查,而构造器属于类的静态部分。
在定义一个工厂方法(用来生成某个类的实例)时,定义了构造器的类型,方法的签名定义了构造器的类型,当执行该工厂方法传入某个类时,会对传入的类中的构造函数进行构造器检查,看是否符合函数签名:
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick();
}
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
return new ctor(hour, minute);
}
class DigitalClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("beep beep");
}
}
class AnalogClock implements ClockInterface {
constructor(h: number, m: number) { }
tick() {
console.log("tick tock");
}
}
let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);
8. 继承接口
和类一样,接口也可以相互继承,而且还可以多根继承:
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
// 使用类型断言强制转换为 Square 类型
let square = {} as Square; // 或者这种写法 let square = <Square>{};
square.color = 'blue';
square.sideLength = 10;
square.penWidth = 5.0;
9. 混合类型
得益于接口能够描述 JavaScript 类丰富的类型,因此混合类型使得一个对象可以同时作为函数和对象使用(JavaScript 中,函数其实也是对象):
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
10. 接口继承类
当接口继承了一个类类型时,它会继承类的成员但不包括其实现,其它类实现该接口时也要同时继承那个类,不能直接去实现该接口。
使用场景并不是很多,可能当构建一个庞大的继承结构时会用到。
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control { }
// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
select() { }
}
网友评论