目录
在 TypeScript 中,interface(接口)是一种用来描述对象形状的结构化类型。与其他语言中的接口概念类似,它定义了对象应该具有的属性和方法,但不提供具体的实现。与类相似,接口可以扩展其他接口,并且可以用于描述函数类型、数组类型等数据形态。在 TypeScript 中接口非常常用,因为它可以帮助开发人员更好地理解代码和规避错误,提高代码的可维护性和可读性。
interface Person {
name: string;
age: number;
}
const person: Person = {
name: 'Tom',
age: 18
};
定义了一个 Person 接口,它描述了一个拥有 name 和 age 属性的对象类型。我们可以将 person 对象的类型声明为 Person,并且确保它遵循了接口的结构。这样,在类型检查阶段,如果我们尝试为 person 对象赋值一个不符合接口的结构,就会在编译时期报错。
在 TypeScript 中,我们可以使用接口来定义函数的类型。函数接口描述了函数的参数类型、返回值类型以及函数本身的结构。通过使用函数接口,我们可以对函数进行类型检查和约束。
interface MathFunc {
(x: number, y: number): number;
}
const add: MathFunc = (a, b) => {
return a + b;
};
const multiply: MathFunc = (a, b) => {
return a * b;
};
上面的代码定义了一个?MathFunc
?函数接口,它描述了接受两个?number
?类型的参数并返回?number
?类型的函数。然后,我们可以使用该接口类型来声明变量?add
?和?multiply
,并实现函数的定义。在变量声明时,我们需要确保函数的结构与函数接口的描述一致,这样就可以进行类型检查和约束。
interface searchFunction {
(kw:string, pgae?:number): string[]
}
interface saveFunction {
(data:object):void
}
const searchAndSave = function(kw:searchFunction, save:saveFunction) {
// code
}
searchAndSave((kw:string) => {
return ['a', 'b', 'c']
}, () => {})
`searchFunction` 描述了一个接受一个字符串类型的关键字参数 `kw` 和一个可选的数字类型的页码参数 `page`,返回一个字符串数组类型的函数。
`saveFunction` 描述了一个接受一个对象类型的参数 `data`,没有返回值的函数。
接下来,通过 `searchAndSave` 函数,将这两个函数接口作为参数进行组合。`searchAndSave` 函数接受一个参数 `kw`,它是一个符合 `searchFunction` 函数接口的函数,并且接受一个参数 `save`,它是一个符合 `saveFunction` 函数接口的函数。在 `searchAndSave` 函数的实现中,可以根据需要使用这两个参数进行相应的操作。
在代码示例的最后一行,调用了 `searchAndSave` 函数,并传入了两个匿名函数作为参数。
第一个匿名函数 `kw => ['a', 'b', 'c']` 符合 `searchFunction` 函数接口的定义,它接受一个字符串参数 `kw`,并返回一个字符串数组。
第二个匿名函数 `() => {}` 是一个空函数,没有实现任何功能,但它符合 `saveFunction` 函数接口的定义,它接受一个对象参数 `data`,没有返回值。
通过这种方式,我们可以在调用 `searchAndSave` 函数时,根据业务需求传入相应的函数,并保证它们的参数和返回值类型的一致性。
在 TypeScript 中,我们可以使用接口来描述类的结构和约束类的属性和方法。这种接口常被称为类接口(class interface)或者构造函数接口(constructor interface)。
类接口主要用于定义类的实例部分的属性和方法。通过定义类接口,我们可以对类的实例进行类型检查和约束。
interface Point {
x: number;
y: number;
move(dx: number, dy: number): void;
}
class MyPoint implements Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
move(dx: number, dy: number) {
this.x += dx;
this.y += dy;
}
}
const point = new MyPoint(1, 2);
point.move(3, 4);
console.log(point.x, point.y); // 输出: 4, 6
我们定义了一个类接口?Point
,它描述了一个具有?x
?和?y
?属性,以及一个用于移动坐标的?move
?方法的类。然后,我们创建了一个类?MyPoint
,该类实现了?Point
?接口。
在?MyPoint
?类中,我们需要确保类的实例具有?x
?和?y
?属性,并实现了?move
?方法的定义。通过实现接口,我们可以在编译时检查类的实例是否符合接口的要求。
最后,我们创建了一个?MyPoint
?类的实例?point
,并进行了一次坐标的移动。我们可以看到,通过类接口的约束,我们可以更好地对类的结构进行定义和约束。
抽象类是一种特殊的类,它不能直接被实例化,而是用于作为其他类的基类(父类)。抽象类可以包含抽象方法和非抽象方法,并且可以提供一些基本的实现,让子类来继承和扩展。
abstract class Point {
abstract x: number;
abstract y: number;
abstract move(dx: number, dy: number): void;
}
class MyPoint extends Point {
x: number;
y: number;
constructor(x: number, y: number) {
super();
this.x = x;
this.y = y;
}
move(dx: number, dy: number) {
this.x += dx;
this.y += dy;
}
}
const point = new MyPoint(1, 2);
point.move(3, 4);
console.log(point.x, point.y); // 输出: 4, 6
在上面的例子中,我们定义了一个抽象类?Point
,它包含了抽象属性?x
?和?y
,以及抽象方法?move
。抽象方法不包含具体的实现,而是留给子类去实现。
然后,我们创建了一个子类?MyPoint
,该类继承自抽象类?Point
。在子类中,我们必须实现抽象类中所有的抽象属性和方法。
最后,我们创建了一个?MyPoint
?类的实例?point
,并进行了一次坐标的移动。输出结果与之前的示例相同。
抽象类和接口是 TypeScript 中用于定义和约束类和对象行为的关键概念,它们有以下几点区别:
1. 实例化: 接口不能被实例化,而抽象类也不能直接被实例化,只能作为其他类的基类。
2. 实现限制: 类可以实现多个接口,但只能继承一个抽象类。
3. 成员实现: 接口中的成员都是抽象的,没有具体的实现细节,而抽象类可以包含具体的成员实现。
4. 属性和方法: 接口可以包含属性、方法、索引签名和构造签名等成员,而抽象类可以包含属性、方法(包括抽象方法和具体方法)、构造函数和访问修饰符等成员。
5. 多态性: 抽象类可以实现多态,通过在基类中定义抽象方法来强制子类必须实现该方法。而接口无法实现多态,只能用于描述对象的结构。
6. 继承关系: 抽象类可以继承其他类或抽象类,也可以实现接口。而接口只能继承其他接口,不能直接继承类。
7. 用途: 抽象类适用于定义一些相似的子类,通过继承抽象类来避免重复代码并实现代码复用。接口常用于描述对象的结构,用于类型检查和约束。
抽象类主要用于定义类的共性和结构,可以包含具体的实现细节;而接口主要用于描述对象的结构,强调规范和约束,不包含具体的实现细节。
抽象类更适合用于具有相似行为的类的继承和扩展,而接口更适合用于对象的类型检查和约束。
构造函数接口用于描述一个类的构造函数的类型。通过构造函数接口,我们可以定义类的构造函数参数的类型和返回的实例类型。
interface Point {
x: number;
y: number;
}
interface PointConstructor {
new (x: number, y: number): Point;
}
class MyPoint implements Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const PointClass: PointConstructor = MyPoint;
const point: Point = new PointClass(1, 2);
console.log(point.x, point.y); // 输出: 1, 2
接口之间可以通过继承来建立关系,使一个接口可以继承另一个或多个接口的成员。接口的继承可以通过关键字?extends
?来实现。
interface Box {
area():number;
length():number;
}
interface Box2 extends Box {
volumn():number;
}
// Box3这个类需要同时实现Box和Box2中的方法
class Box3 implements Box2 {
area() {
return 1
}
length() {
return 2
}
volumn() {
return 3;
}
}
class Box {
protected uname:string = 'zs';
}
interface Box2 extends Box {
pwd:string;
}
class Box3 extends Box implements Box2 {
pwd:string;
constructor(pwd:string) {
super();
this.pwd = pwd
}
showName() {
console.log(this.uname)
console.log(this.pwd)
}
}
let b = new Box3('123456')
b.showName()
继承接口可以实现接口之间的层次关系,使得接口能够更好地组织和描述对象的结构和行为。同时,这也提供了代码共享和重用的机制,有助于增强代码的可读性和可维护性。