《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括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) |