以下是老曹关于TypeScript 最常用的 20 道面试题总结,TS是原生Js的超集,现如今已经是前端面试特别是中高级面试必问的问题,也是大工程项目必须掌握的技术了,以下涵盖类型系统、接口、泛型、类、装饰器、模块等核心知识点。每道题都包含详细解释和代码示例,适合大家对前端开发岗位的 TypeScript 技术面试准备,大家可以随时翻出来查阅背诵和练习!
1. TypeScript 和 JavaScript 的区别是什么?
问题: 解释 TypeScript 相比 JavaScript 增加了哪些特性?
答案:
- 类型系统(静态类型检查)。
- 接口(Interface)与类型别名(Type)。
- 泛型支持。
- 装饰器(Decorators)。
- 对象结构约束与可选属性。
- 更好的 IDE 支持(自动补全、错误提示)。
// 示例:类型检查
function greet(name: string): void {
console.log(`Hello, ${name}`);
}
greet("Tom"); // 正确
greet(123); // 编译报错:参数类型不匹配
2. 如何定义变量并指定其类型?
问题: 定义一个 age
变量,并限制为数字类型。
答案:
使用类型注解语法。
let age: number = 25;
age = "25"; // 报错:不能将字符串赋值给 number 类型
3. 什么是联合类型?如何使用?
问题: 定义一个函数参数可以是 string
或 number
。
答案:
使用 |
表示联合类型。
function printId(id: number | string): void {
console.log(`ID: ${id}`);
}
printId(101); // OK
printId("101"); // OK
printId(true); // 报错
4. 什么是类型断言?如何使用?
问题: 告诉编译器某个值的具体类型。
答案:
使用 <Type>
或 as Type
。
const input = document.getElementById("username") as HTMLInputElement;
input.value = "默认值";
5. 接口(interface
)和类型别名(type
)的区别?
问题: interface Person {}
vs type Person = {}
的区别。
答案:
特性 | interface | type |
---|---|---|
可扩展性 | 支持声明合并 | 不支持 |
支持继承 | 支持 extends | 支持交叉类型 & |
支持基本类型 | ❌ | ✅(如 `type ID = string |
使用场景 | 对象结构建模 | 复杂类型组合 |
// interface
interface User {
name: string;
}
// type
type ID = string | number;
6. 如何定义可选属性?
问题: 在接口中定义一个可选的 age
属性。
答案:
使用 ?
标记可选属性。
interface Person {
name: string;
age?: number; // 可选
}
const p: Person = { name: "Alice" }; // 合法
7. 元组(Tuple)的作用是什么?如何定义?
问题: 创建一个元组表示 [姓名, 年龄]
。
答案:
元组允许你定义数组中元素的类型顺序。
let user: [string, number];
user = ["Alice", 25]; // 合法
user = [25, "Alice"]; // 报错:类型顺序不符
8. 枚举(enum
)有什么用途?如何定义?
问题: 定义一个表示星期的枚举。
答案:
枚举用于命名一组常量值。
enum WeekDay {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
}
console.log(WeekDay.Monday); // 输出: 0
console.log(WeekDay[3]); // 输出: "Thursday"
9. 泛型(Generic)的作用是什么?如何使用?
问题: 写一个通用的 identity
函数,返回传入的任意类型。
答案:
泛型让函数/类/接口具有更强的灵活性和复用性。
function identity<T>(value: T): T {
return value;
}
console.log(identity<string>("hello")); // 输出: "hello"
console.log(identity<number>(123)); // 输出: 123
10. 类型推断(Type Inference)是什么?
问题: 如果没有显式标注类型,TS 如何确定类型?
答案:
TypeScript 会根据赋值自动推断类型。
let message = "Hello"; // 类型自动推断为 string
message = 123; // 报错:不能将 number 赋值给 string
11. 如何定义函数类型?
问题: 定义一个函数类型,接受两个数字,返回一个数字。
答案:
使用类型表达式 type Fn = (a: number, b: number) => number;
type Sum = (a: number, b: number) => number;
const add: Sum = (x, y) => x + y;
console.log(add(2, 3)); // 输出: 5
12. 类型守卫(Type Guard)是什么?如何实现?
问题: 判断一个值是否是数字类型。
答案:
使用 typeof
或自定义谓词函数。
function isNumber(x: any): x is number {
return typeof x === "number";
}
function processValue(value: number | string): void {
if (isNumber(value)) {
console.log(value.toFixed(2));
} else {
console.log(value.toUpperCase());
}
}
13. 如何定义一个类?
问题: 创建一个 Person
类,有 name和 sayHello()
方法。
答案:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
sayHello(): void {
console.log(`Hello, my name is ${this.name}`);
}
}
const p = new Person("Tom");
p.sayHello(); // 输出: Hello, my name is Tom
14. 类中的访问修饰符有哪些?分别代表什么含义?
问题: public
、private
和 protected
的区别?
答案:
public
:任何地方都可以访问(默认)。private
:只能在类内部访问。protected
:可在类及子类中访问。
class Animal {
public name: string;
private secret: string;
protected family: string;
constructor(name: string, secret: string, family: string) {
this.name = name;
this.secret = secret;
this.family = family;
}
}
class Dog extends Animal {
showFamily() {
console.log(this.family); // 合法
// console.log(this.secret); // ILLEGAL
}
}
15. 如何实现接口继承?
问题: 让 Employee
接口继承 Person
接口。
答案:
使用 extends
实现接口继承。
interface Person {
name: string;
}
interface Employee extends Person {
id: number;
}
const emp: Employee = { name: "John", id: 1 };
16. 如何定义只读属性?
问题: 定义一个不可修改的 id
字段。
答案:
使用 readonly
关键字。
interface User {
readonly id: number;
name: string;
}
const user: User = { id: 1, name: "Alice" };
user.id = 2; // 报错:无法修改只读属性
17. 装饰器(Decorator)是什么?如何使用?
问题: 写一个简单的类装饰器。
答案:
装饰器是一种特殊类型的声明,可用于类、方法、属性等。
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling "${key}" with`, args);
return originalMethod.apply(this, args);
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(2, 3);
// 输出: Calling "add" with [2, 3]
18. any
和 unknown
的区别?
问题: any
和 unknown
在类型检查时有何不同?
答案:
any
:绕过所有类型检查。unknown
:必须先做类型检查后才能操作。
let value: unknown;
value = "hello";
if (typeof value === "string") {
console.log(value.toUpperCase()); // 安全地使用
}
let val: any = 123;
val = "abc";
val = true;
console.log(val.foo.bar.baz); // 不会报错(但运行时报错)
19. 如何定义可索引类型?
问题: 创建一个对象,通过字符串索引获取值。
答案:
使用索引签名。
interface StringArray {
[index: number]: string;
}
const arr: StringArray = ["Apple", "Banana"];
console.log(arr[0]); // 输出: "Apple"
interface Dictionary {
[key: string]: number;
}
const data: Dictionary = { one: 1, two: 2 };
20. 如何使用 never
类型?
问题: 写一个总是抛出错误的函数,并返回 never
。
答案:
never
表示永远不会返回值的函数。
function throwError(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
📋 总结表格
编号 | 题目描述 | 知识点 | 示例代码 | 常见考察点 |
---|---|---|---|---|
1 | TS 与 JS 区别 | 类型系统 | function greet(name: string) | 类型安全 |
2 | 定义带类型的变量 | 类型注解 | let age: number = 25 | 基础语法 |
3 | 联合类型 | ` | ` 操作符 | `id: string |
4 | 类型断言 | 强制类型转换 | as HTMLElement | 类型信任机制 |
5 | interface vs type | 类型定义方式 | interface A {} vs type A = {} | 接口设计 |
6 | 可选属性 | ? 修饰符 | age?: number | 数据灵活性 |
7 | 元组类型 | 固定长度数组 | [string, number] | 结构控制 |
8 | 枚举类型 | 枚举值映射 | enum Status { Success, Failed } | 可读性增强 |
9 | 泛型 | 参数化类型 | function identity<T>(value: T) | 通用性 |
10 | 类型推断 | 自动识别类型 | let msg = "hello" | 类型安全 |
11 | 函数类型定义 | 类型签名 | (a: number, b: number) => number | 函数编程 |
12 | 类型守卫 | 运行时判断 | typeof value === 'number' | 类型细化 |
13 | 类定义 | OOP 语法 | class Person { ... } | 面向对象 |
14 | 类成员访问控制 | public , private , protected | private secret: string | 封装原则 |
15 | 接口继承 | 扩展接口 | interface Employee extends Person | 接口设计 |
16 | 只读属性 | readonly | readonly id: number | 数据不变性 |
17 | 装饰器应用 | 类/方法增强 | @log() | 高阶拓展 |
18 | any 与 unknown 区别 | 类型安全性 | value is unknown | 类型防御 |
19 | 可索引类型 | 索引签名 | { [key: string]: number } | 动态对象 |
20 | never 的用途 | 永远不返回 | function throwError(): never | 错误处理 |