面试题答案
一键面试实现区别
- 类的结构化类型:基于对象的实际结构进行类型检查,无需显式声明实现某个特定结构,只要对象具有相应的属性和方法即可。例如:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} barks.`);
}
}
function greet(animal: { name: string; speak(): void }) {
animal.speak();
}
const myDog = new Dog('Buddy');
greet(myDog);
这里Dog
类并没有显式声明实现某个接口,但由于其结构与greet
函数参数期望的结构匹配,所以可以正常使用。
2. 接口:需要类显式声明实现接口,明确表明类满足接口定义的结构。例如:
interface AnimalInterface {
name: string;
speak(): void;
}
class Cat implements AnimalInterface {
name: string;
constructor(name: string) {
this.name = name;
}
speak() {
console.log(`${this.name} meows.`);
}
}
Cat
类通过implements
关键字明确表明实现了AnimalInterface
接口。
应用场景区别
- 类的结构化类型:适用于更灵活、动态的类型匹配场景,不需要严格的继承或实现关系,只关注对象的实际结构。比如在函数参数类型定义上,当我们只关心传入对象是否具有某些属性和方法,而不关心其具体类时,可使用结构化类型。像上述
greet
函数,不管是Animal
类还是Dog
类的实例,只要结构匹配就能传入。 - 接口:适用于需要明确契约和类型约束的场景,确保类具有特定的结构。常用于多个类需要遵循相同结构的情况,比如在大型项目中,不同模块的类可能需要实现相同接口以保证一致性。
优先选择类的结构化类型的情况
- 临时或一次性的类型匹配:当在某个局部函数中,只需要对传入对象的结构进行简单验证,而不需要定义一个正式的接口时,使用结构化类型更便捷。例如:
function printDetails(obj: { title: string; content: string }) {
console.log(`Title: ${obj.title}, Content: ${obj.content}`);
}
const article = { title: 'New Article', content: 'This is the content...' };
printDetails(article);
这里没有必要为这种一次性使用的结构定义接口。 2. 处理来自外部库且难以或无需定义接口的对象:当使用一些外部库返回的对象,其结构明确但不适合定义接口时,结构化类型可直接使用。比如某些第三方 API 返回的数据对象,我们只关心其特定的属性结构来进行处理,而不是为其专门定义接口。