在 TypeScript 中,接口(interface
)是一种用于定义对象结构的契约,它描述了一个对象的形状,包括它的属性和方法的类型。接口是 TypeScript 中实现 面向对象编程 的一个重要工具,广泛用于类型检查、定义类的结构、函数签名等。接口并不提供实现,仅仅定义了如何使用它们的约定,且可以通过实现接口的方式进行类型检查。
下面将详细介绍 TypeScript 中的接口,包括接口的定义、扩展、实现、可选属性、只读属性等。
1. 基本的接口定义
接口通过 interface
关键字来定义,通常用于指定对象的形状,包括属性的类型、方法的签名等。
定义接口
interface Person {
name: string; // 必须存在的属性
age: number; // 必须存在的属性
}
Person
接口描述了一个具有name
和age
属性的对象,name
是string
类型,age
是number
类型。
使用接口
const person: Person = {
name: "Alice",
age: 30
};
- 使用接口时,我们声明了
person
变量,确保它符合Person
接口的要求,即具有name
和age
属性,并且类型正确。
2. 接口的可选属性
接口中的属性默认是必需的,但你可以通过 ?
来声明可选属性,这样对象就可以没有这个属性。
可选属性
interface Person {
name: string;
age?: number; // 可选属性
}
const person1: Person = { name: "Alice" }; // 合法,因为 age 是可选的
const person2: Person = { name: "Bob", age: 30 }; // 合法
age?
:表示age
是可选的,person1
不需要提供age
属性。
3. 接口的只读属性
你可以通过 readonly
关键字来声明只读属性,这意味着属性的值在对象创建后不能被修改。
只读属性
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
console.log(point.x); // 10
// point.x = 30; // 错误: 无法分配到 "x" ,因为它是只读属性
readonly
:标记属性为只读,不能在对象实例化后修改该属性的值。
4. 接口的函数签名
接口不仅可以定义对象的属性,还可以定义方法的签名。方法签名在接口中表示为函数类型。
函数签名
interface Greeter {
greet(name: string): string;
}
class EnglishGreeter implements Greeter {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
const greeter = new EnglishGreeter();
console.log(greeter.greet("Alice")); // "Hello, Alice!"
Greeter
接口定义了一个greet
方法,接受一个name
参数并返回一个字符串。EnglishGreeter
类实现了这个接口,并提供了greet
方法的具体实现。
5. 接口的继承
接口可以继承其他接口,继承后,接口会包含父接口的所有属性和方法签名。通过继承,接口可以复用其他接口的定义。
接口继承
interface Animal {
name: string;
makeSound(): void;
}
interface Dog extends Animal {
breed: string;
}
const dog: Dog = {
name: "Buddy",
breed: "Golden Retriever",
makeSound: () => console.log("Bark")
};
console.log(dog.name); // "Buddy"
console.log(dog.breed); // "Golden Retriever"
dog.makeSound(); // "Bark"
Dog
接口继承了Animal
接口,除了继承name
和makeSound()
外,还增加了breed
属性。
6. 接口实现类的约定
类可以实现接口,这意味着类必须实现接口中定义的所有属性和方法。实现接口可以确保类符合接口的约定。
类实现接口
interface Person {
name: string;
greet(): void;
}
class Employee implements Person {
constructor(public name: string, public role: string) {}
greet(): void {
console.log(`Hello, my name is ${this.name}. I am a ${this.role}.`);
}
}
const employee = new Employee("Alice", "Developer");
employee.greet(); // 输出: Hello, my name is Alice. I am a Developer.
Employee
类实现了Person
接口,必须提供name
属性和greet
方法的实现。
7. 接口与函数类型
接口可以用于定义函数类型,函数类型的接口可以用来描述函数的参数类型和返回值类型。
函数类型接口
interface Sum {
(x: number, y: number): number;
}
const add: Sum = (x, y) => x + y;
console.log(add(5, 3)); // 输出: 8
Sum
接口定义了一个函数签名,要求传入两个number
类型的参数并返回number
类型的结果。add
变量是Sum
类型,符合该接口的函数签名。
8. 接口的混合类型(接口合并)
接口不仅可以继承其他接口,还可以通过声明合并来扩展已有的接口。这种特性使得 TypeScript 在定义多种行为时非常灵活。
接口合并
interface Box {
width: number;
height: number;
}
interface Box {
depth: number;
}
const myBox: Box = {
width: 10,
height: 20,
depth: 30
};
console.log(myBox.width); // 10
console.log(myBox.height); // 20
console.log(myBox.depth); // 30
- 在上面的例子中,
Box
接口通过声明合并,将depth
属性添加到了原本只有width
和height
的接口中。 myBox
对象符合合并后的Box
接口。
9. 索引签名
有时你可能需要定义对象的动态属性名,TypeScript 提供了 索引签名 来实现这一功能。索引签名允许你定义任意属性。
索引签名
interface Dictionary {
[key: string]: string;
}
const dict: Dictionary = {
"name": "Alice",
"age": "30"
};
console.log(dict["name"]); // "Alice"
console.log(dict["age"]); // "30"
[key: string]: string
:表示Dictionary
类型的对象可以有任意数量的字符串类型的键,每个键对应的值是string
类型。
10. 联合类型和接口
接口中的属性类型可以使用 联合类型,表示属性可以是多种类型之一。
联合类型
interface Contact {
name: string;
phone: string | number; // phone 可以是 string 或者 number 类型
}
const contact1: Contact = {
name: "Alice",
phone: "123-456-7890"
};
const contact2: Contact = {
name: "Bob",
phone: 9876543210
};
phone
属性可以是string
或number
类型。
11. 总结
- 接口(
interface
)定义了一个对象的结构,可以包含属性、方法、构造函数签名等。 - 可选属性和只读属性允许你在接口中定义更灵活的规则。
- 接口可以用于函数签名,定义函数的参数和返回类型。
- 接口支持继承,可以扩展其他接口。
- 实现接口的类必须提供接口中定义的所有方法和属性。
- 索引签名允许你定义具有动态属性名的对象。
- 联合类型和接口的结合使得类型定义更加灵活。
通过接口,TypeScript 强化了静态类型检查,并提供了对对象结构的精确控制,广泛应用于定义对象、函数、类等的数据结构和行为。