TypeScript 面试题及详细答案 100题 (21-30)-- 接口(Interface)
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux… 。
文章目录
- 一、本文面试题目录
- 21. 接口的核心作用是什么?如何用接口定义对象的结构?
- 22. 如何定义接口的可选属性、只读属性?举例说明。
- 23. 接口如何描述函数类型?与直接定义函数类型有何区别?
- 24. 什么是可索引接口?如何定义数字索引和字符串索引接口?
- 25. 接口之间如何实现继承?一个接口可以继承多个接口吗?
- 26. 接口与类的关系是什么?类如何实现接口?一个类可以实现多个接口吗?
- 27. 什么是接口合并?哪些场景下会发生接口合并?
- 28. 接口可以定义静态属性或方法吗?为什么?
- 29. 如何用接口描述数组的“只读”特性?
- 30. 接口与类型别名(Type Alias)在定义对象结构时的核心区别是什么?
- 二、100道TypeScript面试题目录列表
一、本文面试题目录
21. 接口的核心作用是什么?如何用接口定义对象的结构?
- 答案:接口(Interface)的核心作用是定义一种契约,用于描述对象的结构(属性和方法)、函数类型、类的形状等,确保代码符合预期的规范。它仅负责类型检查,不会被编译为JavaScript代码。
用接口定义对象结构时,需声明对象应包含的属性名称、类型,以及可选的方法(仅需定义方法签名,无需实现)。
- 示例代码:
// 定义对象结构的接口
interface User {name: string; // 必选属性age: number; // 必选属性greet: () => string; // 方法签名
}// 实现接口的对象
const user: User = {name: "Alice",age: 30,greet() {return `Hello, my name is ${this.name}`;}
};// 不符合接口的对象会报错
const invalidUser: User = {name: "Bob" // 报错:缺少age和greet
};
22. 如何定义接口的可选属性、只读属性?举例说明。
-
答案:
- 可选属性:在属性名后加
?
,表示该属性可存在或不存在。 - 只读属性:在属性名前加
readonly
,表示该属性初始化后不可修改。
- 可选属性:在属性名后加
-
示例代码:
interface Book {readonly id: number; // 只读属性(初始化后不可改)title: string; // 必选属性author: string; // 必选属性publisher?: string; // 可选属性(可省略)pages?: number; // 可选属性
}// 符合接口的对象
const book1: Book = {id: 1001,title: "TypeScript Guide",author: "John Doe"// publisher和pages可选,可省略
};const book2: Book = {id: 1002,title: "JS Basics",author: "Jane Smith",publisher: "Tech Press",pages: 300
};// 错误示例
book1.id = 2000; // 报错:只读属性不可修改
book2.publisher = "New Press"; // 允许:可选属性可修改
23. 接口如何描述函数类型?与直接定义函数类型有何区别?
- 答案:接口描述函数类型时,需定义一个函数签名(参数类型和返回值类型),格式为
(参数: 类型) => 返回值类型
。
区别:
-
接口描述函数类型更具语义性,可复用(如作为参数或返回值类型);
-
直接定义函数类型(如
type Func = (a: number) => number
)更简洁,适合临时使用。 -
示例代码:
// 用接口描述函数类型
interface MathOperation {(a: number, b: number): number;
}// 实现接口的函数
const add: MathOperation = (x, y) => x + y;
const multiply: MathOperation = (x, y) => x * y;// 直接定义函数类型
type StringFormatter = (str: string) => string;
const toUpper: StringFormatter = (s) => s.toUpperCase();// 接口的复用性
function calculate(operation: MathOperation, a: number, b: number): number {return operation(a, b);
}
console.log(calculate(add, 2, 3)); // 5
24. 什么是可索引接口?如何定义数字索引和字符串索引接口?
-
答案:可索引接口用于描述可通过索引访问的对象(如数组、字典),定义索引类型(数字或字符串)和对应的值类型。
-
数字索引接口:索引为
number
类型,常用于描述数组或类数组对象。 -
字符串索引接口:索引为
string
类型,常用于描述字典(键值对)对象。 -
示例代码:
// 数字索引接口(类似数组)
interface NumberArray {[index: number]: number; // 索引为number,值为number
}
const arr: NumberArray = [1, 2, 3];
console.log(arr[0]); // 1
// arr[0] = "a"; // 报错:值必须为number// 字符串索引接口(类似字典)
interface StringMap {[key: string]: string; // 索引为string,值为string
}
const dict: StringMap = {name: "Alice",city: "Beijing"
};
console.log(dict["name"]); // "Alice"
// dict["age"] = 30; // 报错:值必须为string// 混合索引(字符串索引值类型需兼容数字索引值类型)
interface MixedIndex {[key: string]: number | string;[index: number]: number; // 数字索引值类型必须是字符串索引值类型的子类型
}
25. 接口之间如何实现继承?一个接口可以继承多个接口吗?
-
答案:接口通过
extends
关键字实现继承,子接口会继承父接口的所有属性和方法。一个接口可以继承多个接口,用逗号分隔父接口列表。 -
示例代码:
// 父接口1
interface Person {name: string;age: number;
}// 父接口2
interface Contact {phone: string;email?: string;
}// 继承单个接口
interface Student extends Person {studentId: number;
}// 继承多个接口
interface Teacher extends Person, Contact {teacherId: number;subject: string;
}// 实现Student接口
const student: Student = {name: "Bob",age: 18,studentId: 1001
};// 实现Teacher接口(需包含Person和Contact的所有属性)
const teacher: Teacher = {name: "Ms. Lee",age: 35,phone: "123-4567",teacherId: 2001,subject: "Math"
};
26. 接口与类的关系是什么?类如何实现接口?一个类可以实现多个接口吗?
- 答案:接口可以约束类的结构(属性和方法),类通过
implements
关键字实现接口,必须实现接口中声明的所有属性和方法(除可选属性外)。
一个类可以实现多个接口,用逗号分隔接口列表,需满足所有接口的约束。
- 示例代码:
// 定义接口
interface Runable {speed: number;run(): void;
}interface Swimmable {swimSpeed: number;swim(): void;
}// 类实现单个接口
class Dog implements Runable {speed: number = 20;run() {console.log(`Dog runs at ${this.speed}km/h`);}
}// 类实现多个接口
class Duck implements Runable, Swimmable {speed: number = 10;swimSpeed: number = 5;run() {console.log(`Duck runs at ${this.speed}km/h`);}swim() {console.log(`Duck swims at ${this.swimSpeed}km/h`);}
}// 错误示例:未完全实现接口
class Cat implements Runable {speed: number = 15;// 缺少run()方法,报错
}
27. 什么是接口合并?哪些场景下会发生接口合并?
- 答案:接口合并(Interface Merging)是TypeScript的特性,指多个同名接口会自动合并为一个接口,合并后的接口包含所有同名接口的成员。
发生场景:
- 同一作用域内定义多个同名接口;
- 不同文件中定义的同名接口(全局作用域或模块内)。
- 示例代码:
// 同一作用域内的接口合并
interface Car {brand: string;
}interface Car {model: string;year?: number; // 可选属性
}// 合并后等价于:
// interface Car {
// brand: string;
// model: string;
// year?: number;
// }const myCar: Car = {brand: "Toyota",model: "Camry"
};// 合并函数成员(需函数重载)
interface Calculator {compute(a: number, b: number): number;
}interface Calculator {compute(a: string, b: string): string; // 函数重载
}const calc: Calculator = {compute(a: any, b: any): any {if (typeof a === "number" && typeof b === "number") {return a + b;}return a + b;}
};
28. 接口可以定义静态属性或方法吗?为什么?
- 答案:接口不能定义静态属性或方法。原因是接口的设计目标是约束实例的结构,而静态成员属于类本身而非实例,因此接口无法约束类的静态部分。
若需约束类的静态成员,可使用“工厂模式”或结合类型别名与构造函数类型。
- 示例代码:
// 错误:接口不能定义静态成员
interface MyClass {static staticProp: number; // 报错instanceMethod(): void;
}// 正确方式:用类型别名约束构造函数(静态部分)
type MyClassConstructor = {new (): MyClassInstance; // 实例类型staticProp: number; // 静态属性staticMethod(): void; // 静态方法
};interface MyClassInstance {instanceMethod(): void; // 实例方法
}// 实现类
class MyClass implements MyClassInstance {static staticProp: number = 10;static staticMethod() {console.log("Static method");}instanceMethod() {console.log("Instance method");}
}// 验证静态成员
const ctor: MyClassConstructor = MyClass;
console.log(ctor.staticProp); // 10
29. 如何用接口描述数组的“只读”特性?
-
答案:可通过定义带
readonly
修饰符的数字索引接口,或直接使用TypeScript内置的ReadonlyArray<T>
接口,来描述只读数组(不可修改长度或元素)。 -
示例代码:
// 自定义只读数组接口
interface ReadonlyNumberArray {readonly [index: number]: number;readonly length: number; // 长度也只读
}// 使用内置ReadonlyArray<T>
const readonlyArr: ReadonlyArray<number> = [1, 2, 3];
// 或简写:readonly number[]
const readonlyArr2: readonly number[] = [4, 5, 6];// 错误操作(只读数组不允许修改)
readonlyArr[0] = 10; // 报错
readonlyArr.push(4); // 报错(无push方法,因会修改数组)
readonlyArr.length = 0; // 报错// 转换为普通数组后可修改
const mutableArr: number[] = [...readonlyArr];
mutableArr[0] = 10; // 允许
30. 接口与类型别名(Type Alias)在定义对象结构时的核心区别是什么?
-
答案:接口与类型别名在定义对象结构时的核心区别如下:
- 扩展性:接口可通过
extends
继承,也可通过重复定义合并;类型别名不可继承,也不能合并,需用&
(交叉类型)扩展。 - 实现:类可通过
implements
实现接口;类不能实现类型别名(除非类型别名是对象或接口类型)。 - 命名提示:接口在错误信息中显示原名;类型别名可能被展开显示。
- 适用场景:接口适合定义对象/类的结构(可扩展);类型别名适合联合类型、交叉类型等复杂组合。
- 扩展性:接口可通过
-
示例代码:
// 接口扩展(extends)
interface A { x: number }
interface B extends A { y: string }// 类型别名扩展(交叉类型)
type C = { x: number }
type D = C & { y: string }// 接口合并
interface E { a: number }
interface E { b: string } // 合并为 { a: number; b: string }// 类型别名不能合并(重复定义报错)
// type F = { a: number }
// type F = { b: string } // 报错// 类实现接口
class MyClass implements A {x: number = 10;
}// 类实现类型别名(仅当类型别名为对象类型时允许)
class MyClass2 implements C {x: number = 20;
}
二、100道TypeScript面试题目录列表
文章序号 | TypeScript面试题100道 |
---|---|
1 | TypeScript面试题及详细答案100道(01-10) |
2 | TypeScript面试题及详细答案100道(11-20) |
3 | TypeScript面试题及详细答案100道(21-30) |
4 | TypeScript面试题及详细答案100道(31-40) |
5 | TypeScript面试题及详细答案100道(41-50) |
6 | TypeScript面试题及详细答案100道(51-60) |
7 | TypeScript面试题及详细答案100道(61-70) |
8 | TypeScript面试题及详细答案100道(71-80) |
9 | TypeScript面试题及详细答案100道(81-90) |
10 | TypeScript面试题及详细答案100道(91-100) |