(一)定义
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。
我的理解是,用接受来处理比较复杂的类型检查。参数和函数类型都可以。
(二)类型
1、最简单的写法
1)没有接口时,这样书写;
function printLabel(labelledobj: { label: string }) {
console.log(labelledobj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
2)改为接口的写法后,如下:
interface LabelledValue {
label: string;
}
function printLabel(labelledobj: LabelledValue) {
console.log(labelledobj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
我感觉这样会比较清晰的表示函数参数的类型。
也就是说,接口的作用之一是来检验函数参数类型的一个结构(写法)。
2、接口之描述参数
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;
}
let mySquare = createSquare({color: "black"});
2)只读属性:一些对象属性只能在对象刚刚创建的时候修改其值。
interface Point {
readonly x: number;
readonly y: number;
}
3)例外:
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}
let mySquare = createSquare({ colour: "red", width: 100 });
如上,会出现报错。
TypeScript会认为这段代码可能存在bug。 对象字面量会被特殊对待而且会经过 额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。
官方这样解释,但我没看懂。
解决方案有两个:
第一,使用类型断言:
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
第二,使用接口额外参数定义:
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
3、接口之描述函数
基本用法:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
上面的方法等价于
let mySearch: SearchFunc;
mySearch = function(src: string, sub: string): boolean {
let result = src.search(sub);
return result > -1;
}
好处就是,更明确的表示出函数的类型。
4、类类型
1)实现接口
与C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约
interface ClockInterface {
currentTime: Date;
}
class Clock implements ClockInterface {
currentTime: Date;
constructor(h: number, m: number) { }
}
也可以先定义再实现
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
类是具有两个类型的:静态部分的类型和实例的类型;
其中,constructor存在于类的静态部分;
new是动态部分;
一个复杂一些的例子
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);
因为
createClock
的第一个参数是ClockConstructor
类型,在createClock(AnalogClock, 7, 32)
里,会检查AnalogClock
是否符合构造函数签名。
5、继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
1)一个简单例子
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
2)一个接口可以继承多个接口
interface Shape {
color: string;
}
interface Penstroke {
penWidth: number;
}
interface Square extends Shape, Penstroke {
sideLength: number;
}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
6、混合类型
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;
7、接口继承类
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() { }
}
class TextBox extends Control {
select() { }
}
// 错误:“Image”类型缺少“state”属性。
class Image implements SelectableControl {
select() { }
}
class Location {
}
这里有疑问:
继承和接口有什么区别,在什么场景下使用?
存疑
extends是继承;
implements是接口;
父类和子类是通过接口实现的?还是通过继承实现的?
明显是通过继承实现的,也就是extends;
在上面的例子里,
SelectableControl
包含了Control
的所有成员,包括私有成员state
。 因为state
是私有成员,所以只能够是Control
的子类们才能实现SelectableControl
接口。 因为只有Control
的子类才能够拥有一个声明于Control
的私有成员state
,这对私有成员的兼容性是必需的。在
Control
类内部,是允许通过SelectableControl
的实例来访问私有成员state
的。 实际上,SelectableControl
接口和拥有select
方法的Control
类是一样的。Button
和TextBox
类是SelectableControl
的子类(因为它们都继承自Control
并有select
方法),但Image
和Location
类并不是这样的。
可预见,Location 也会报错。
参考资料:
https://www.tslang.cn/docs/handbook/interfaces.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。