40. TS接口
1. 定义
TypeScript
中的接口是一种抽象结构,用于定义对象的类型。接口定义了对象应该包含的属性和方法,但不提供实现。
TypeScript
的接口类似于其他编程语言中的接口或抽象类,但不同于它们,接口可以描述对象的形状,包括对象具有哪些属性和方法、属性和方法的类型和返回值类型等。
2. 实现
1. 属性接口
属性接口是一种描述对象属性的接口,用来规范对象的属性名、属性类型和属性值等信息。以下是一个属性接口的示例:
interface Person {
name: string;
age: number;
}
function greet(person: Person) {
console.log(`Hello, my name is ${person.name} and I'm ${person.age} years old.`);
}
let john: Person = {
name: "John",
age: 18
};
greet(john); // 输出:Hello, my name is John and I'm 18 years old.
在这个示例中,我们定义了一个 Person
接口,它包含了一个 name
属性和一个 age
属性。然后,我们创建了一个 john
对象,它符合 Person
接口的定义,并将它传递给 greet
函数进行问候。由于 john
对象的属性和类型与 Person
接口的定义一致,所以可以顺利通过编译并输出问候语。
2. 可选属性
有时候我们需要定义一个对象的属性是可选的,即这个属性可以存在也可以不存在。在 TypeScript 中,我们可以使用 ? 符号来表示一个属性是可选的。以下是一个可选属性的示例:
interface Person {
name: string;
age?: number;
}
function greet(person: Person) {
console.log(`Hello, my name is ${person.name}${person.age ? " and I'm " + person.age + " years old." : "."}`);
}
let john: Person = {
name: "John"
};
let jane: Person = {
name: "Jane",
age: 20
};
greet(john); // 输出:Hello, my name is John.
greet(jane); // 输出:Hello, my name is Jane and I'm 20 years old.
在这个示例中,我们将 age
属性定义为可选的,即可以存在也可以不存在。然后,在 greet
函数中,我们使用了一个三目运算符来判断 age
属性是否存在,并根据不同的情况输出不同的问候语。
3. 只读属性
只读属性,即属性只能在对象创建时被赋值,以后不能被修改。使用 readonly 关键字来定义只读属性。以下是一个定义只读属性的接口示例:
interface Point {
readonly x: number;
readonly y: number;
}
let p: Point = { x: 10, y: 20 };
console.log(p.x); // 输出 10
p.x = 5; // 报错,因为 x 是只读属性
在这个示例中,我们定义了一个 Point
接口,它具有两个只读属性 x
和 y
。然后,我们创建了一个符合 Point
接口的对象 p
,并赋值了两个属性。最后,我们尝试修改 p.x
的值,但 TypeScript
报错,因为 x
是只读属性,不能被修改。
注意:只读属性不允许被赋值后再修改,但是如果一个只读属性是一个对象,那么该对象中的属性是可以被修改的,因为只读属性只是保证属性引用不变,而不是属性值不变。
4. 函数接口
函数接口是一种描述函数类型的接口,用来规范函数的参数类型和返回值类型等信息。以下是一个函数接口的示例:
interface Greeter {
(name: string): string;
}
let greet: Greeter = function(name: string) {
return `Hello, ${name}!`;
};
console.log(greet("John")); // 输出:Hello, John!
在这个示例中,我们定义了一个 Greeter
接口,它表示一个参数为 name
,返回值为字符串的函数类型。然后,我们创建了一个名为 greet
的函数,并将它赋值给 Greeter
接口,这样 greet
就符合 Greeter
接口的定义了。最后,我们调用 greet
函数并输出它的返回值。
5. 可索引接口
可索引接口是一种描述数组和对象的接口,用来规范它们的索引类型和索引值类型等信息。以下是一个可索引接口的示例:
interface StringArray {
[index: number]: string;
}
let colors: StringArray = ["Red", "Green", "Blue"];
console.log(colors[0]); // 输出:Red
在这个示例中,我们定义了一个 StringArray
接口,它表示一个索引为 number
类型,值为 string
类型的数组。然后,我们创建了一个 colors
数组,并将它赋值给 StringArray
接口,这样 colors
就符合 StringArray
接口的定义了。最后,我们输出 colors
数组的第一个元素。
6. 类接口
类接口是一种描述类类型的接口,用来规范类的成员和属性等信息。以下是一个类接口的示例:
interface Person {
name: string;
age: number;
greet(name: string): void;
}
class Student implements Person {
constructor(public name: string, public age: number) {}
greet(name: string) {
console.log(`Hello, ${name}, I'm ${this.name}.`);
}
}
let john: Person = new Student("John", 18);
john.greet("Jane"); // 输出:Hello, Jane, I'm John.
在这个示例中,我们定义了一个 Person
接口,它表示一个具有 name
属性、age
属性和 greet
方法的类类型。然后,我们创建了一个 Student
类,并将它实现 Person
接口,这样 Student
就符合 Person
接口的定义了。最后,我们创建了一个 john
对象,并调用它的 greet
方法输出问候语。由于 john
对象是 Student
类的实例,而 Student
类实现了 Person
接口,所以 john
对象也符合 Person
接口的定义。
7. 继承接口
继承接口是一种描述接口继承关系的接口,用来规范接口之间的关系。以下是一个继承接口的示例:
interface Shape {
color: string;
}
interface Circle extends Shape {
radius: number;
}
let circle: Circle = {
color: "Red",
radius: 10
};
console.log(circle.color); // 输出:Red
console.log(circle.radius); // 输出:10
在这个示例中,我们定义了一个 Shape
接口,它表示一个具有 color
属性的形状。然后,我们定义了一个 Circle
接口,并使用 extends
关键字将它继承自 Shape
接口,这样 Circle
就具有了 color
属性和 radius
属性。最后,我们创建了一个 circle
对象,并输出它的 color
属性和 radius
属性。
8. 混合接口
混合接口是一种同时描述函数、类和对象等多种类型的接口,用来规范它们的成员和属性等信息。以下是一个混合接口的示例:
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function createCounter(): Counter {
let counter = function(start: number) {} as Counter;
counter.interval = 10;
counter.reset = function() {};
return counter;
}
let c = createCounter();
c(10);
c.reset();
c.interval = 5.0;
在这个示例中,我们定义了一个 Counter
接口,它表示一个具有函数类型、interval
属性和 reset
方法的接口。然后,我们创建了一个 createCounter
函数,它返回一个符合 Counter
接口的对象。这个对象既是一个函数,也具有 interval
属性和 reset
方法。最后,我们调用这个对象的函数、方法和属性,并输出它们的返回值。