TypeScript 高频面试题
一、基础概念
1. 什么是 TypeScript?
TypeScript 是 JavaScript 的超集,添加了静态类型系统和其他特性。它由 Microsoft 开发,旨在提高代码的可维护性和可扩展性。TypeScript 代码需要编译为 JavaScript 才能在浏览器或 Node.js 中运行。
2. TypeScript 与 JavaScript 的区别?
- 类型系统:TypeScript 有静态类型检查,JavaScript 是动态类型
- 编译:TypeScript 需要编译为 JavaScript 才能运行
- 特性:TypeScript 支持接口、泛型、枚举等特性
- 工具支持:TypeScript 提供更好的 IDE 支持和类型提示
- 兼容性:TypeScript 兼容所有 JavaScript 代码
3. TypeScript 的类型系统有什么优势?
- 类型安全:编译时发现类型错误,减少运行时错误
- 代码提示:IDE 提供更准确的代码提示和自动补全
- 重构支持:更安全的代码重构
- 可读性:类型注解使代码更易于理解
- 维护性:减少代码错误,提高代码可维护性
4. TypeScript 的基本类型有哪些?
- 原始类型:string、number、boolean、null、undefined、symbol、bigint
- 对象类型:object、array、function、class、interface
- 联合类型:string | number
- 交叉类型:A & B
- 字面量类型:'hello'、42、true
- any 类型:任意类型
- unknown 类型:未知类型,需要类型检查
- void 类型:无返回值
- never 类型:永远不会返回的值
- tuple 类型:固定长度的数组
5. 什么是接口(Interface)?
接口是 TypeScript 中用于定义对象结构的类型,它描述了对象应该有哪些属性和方法。接口只在编译时进行类型检查,不会生成 JavaScript 代码。
6. 接口与类型别名(Type Alias)的区别?
- 接口:只能描述对象类型,支持继承,可被合并
- 类型别名:可以描述任意类型,不支持继承,不能被合并
7. 什么是泛型(Generics)?
泛型是 TypeScript 中用于创建可重用组件的工具,它允许在定义函数、接口或类时使用类型参数,使代码更加灵活和可复用。
8. 什么是枚举(Enum)?
枚举是 TypeScript 中用于定义命名常量集合的类型,它可以提高代码的可读性和可维护性。
9. 什么是类型断言(Type Assertion)?
类型断言是 TypeScript 中用于告诉编译器某个值的类型的方式,它不会运行时检查,只是编译时的提示。
10. 什么是类型守卫(Type Guard)?
类型守卫是 TypeScript 中用于在运行时检查变量类型的表达式,它可以帮助编译器在编译时确定变量的类型。
二、核心特性
11. 什么是接口继承?
接口继承是 TypeScript 中用于扩展接口的方式,一个接口可以继承另一个或多个接口的属性和方法。
12. 什么是泛型约束?
泛型约束是 TypeScript 中用于限制泛型类型范围的方式,它可以确保泛型类型具有某些特定的属性或方法。
13. 什么是模块解析?
模块解析是 TypeScript 中用于查找导入模块的过程,它支持 CommonJS 和 ES6 模块系统。
14. 什么是声明文件(.d.ts)?
声明文件是 TypeScript 中用于描述 JavaScript 库类型的文件,它可以为没有类型定义的 JavaScript 库提供类型支持。
15. 什么是命名空间(Namespace)?
命名空间是 TypeScript 中用于组织代码的方式,它可以避免命名冲突,类似于模块。
16. 什么是装饰器(Decorator)?
装饰器是 TypeScript 中用于修改类、方法、属性或参数的元编程特性,它可以在不修改原始代码的情况下增强代码功能。
17. 什么是抽象类(Abstract Class)?
抽象类是 TypeScript 中用于定义基类的方式,它不能被实例化,只能被继承,用于定义子类必须实现的方法。
18. 什么是访问修饰符(Access Modifiers)?
访问修饰符是 TypeScript 中用于控制类成员访问权限的关键字,包括 public、private、protected。
19. 什么是 readonly 修饰符?
readonly 修饰符是 TypeScript 中用于定义只读属性的关键字,它可以确保属性在初始化后不能被修改。
20. 什么是可选链操作符(Optional Chaining)?
可选链操作符(?.)是 TypeScript 中用于安全访问对象属性的操作符,它可以在属性不存在时返回 undefined,而不是抛出错误。
三、类型系统
21. 什么是类型推断?
类型推断是 TypeScript 中用于自动推断变量类型的机制,它可以根据变量的初始化值或使用上下文推断出变量的类型。
22. 什么是类型兼容性?
类型兼容性是 TypeScript 中用于判断两个类型是否可以相互赋值的规则,它基于结构子类型化。
23. 什么是类型守卫?
类型守卫是 TypeScript 中用于在运行时检查变量类型的表达式,它可以帮助编译器在编译时确定变量的类型。
24. 什么是类型谓词?
类型谓词是 TypeScript 中用于在类型守卫函数中指定变量类型的返回类型,它的形式为 parameter is Type。
25. 什么是条件类型?
条件类型是 TypeScript 中用于根据条件生成类型的类型操作符,它的形式为 T extends U ? X : Y。
26. 什么是映射类型?
映射类型是 TypeScript 中用于基于现有类型创建新类型的方式,它可以遍历现有类型的属性并修改它们。
27. 什么是索引签名?
索引签名是 TypeScript 中用于定义对象可以具有任意属性的方式,它的形式为 [key: string]: type。
28. 什么是函数重载?
函数重载是 TypeScript 中用于定义多个函数签名的方式,它可以根据不同的参数类型和数量调用不同的函数实现。
29. 什么是类型保护函数?
类型保护函数是 TypeScript 中用于在运行时检查变量类型的函数,它返回一个类型谓词。
30. 什么是模块增强?
模块增强是 TypeScript 中用于扩展现有模块类型定义的方式,它可以为现有模块添加新的属性和方法。
四、配置和工具
31. TypeScript 的配置文件是什么?
TypeScript 的配置文件是 tsconfig.json,它用于配置 TypeScript 编译器的选项。
32. tsconfig.json 中的常用配置选项有哪些?
- target:编译目标 JavaScript 版本
- module:模块系统
- strict:启用严格模式
- esModuleInterop:启用 ES 模块互操作性
- outDir:输出目录
- rootDir:源文件目录
- include:包含的文件
- exclude:排除的文件
33. 如何在项目中使用 TypeScript?
- 初始化项目:
npm init -y - 安装 TypeScript:
npm install typescript --save-dev - 创建 tsconfig.json:
npx tsc --init - 编译 TypeScript:
npx tsc - 运行 JavaScript:
node dist/index.js
34. 如何在 React 项目中使用 TypeScript?
- 创建项目:
npx create-react-app my-app --template typescript - 或添加 TypeScript:
npm install typescript @types/react @types/react-dom --save-dev
35. 如何在 Vue 项目中使用 TypeScript?
- 创建项目:
npm init vite@latest my-app -- --template vue-ts - 或添加 TypeScript:
npm install typescript @vue/compiler-sfc --save-dev
36. 如何在 Node.js 项目中使用 TypeScript?
- 安装依赖:
npm install typescript @types/node --save-dev - 创建 tsconfig.json:
npx tsc --init - 编译和运行:
npx tsc && node dist/index.js
37. 什么是 ts-node?
ts-node 是一个用于直接运行 TypeScript 文件的工具,它可以在运行时编译 TypeScript 代码,无需手动编译。
38. 什么是 ESLint 和 Prettier?
- ESLint:用于检查代码质量和风格的工具
- Prettier:用于格式化代码的工具
39. 如何配置 ESLint 和 Prettier 以支持 TypeScript?
- 安装依赖:
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier --save-dev - 创建 .eslintrc.js:配置 ESLint 规则
- 创建 .prettierrc:配置 Prettier 规则
40. 什么是类型定义文件(@types)?
类型定义文件是 TypeScript 中用于描述 JavaScript 库类型的文件,它们通常由 DefinitelyTyped 社区维护,通过 @types 包发布。
五、高级特性
41. 什么是泛型工具类型?
泛型工具类型是 TypeScript 中内置的用于操作类型的工具,如 Partial、Required、Readonly、Pick、Omit 等。
42. 什么是递归类型?
递归类型是 TypeScript 中用于定义自引用类型的方式,它可以用于定义嵌套的数据结构。
43. 什么是分布式条件类型?
分布式条件类型是 TypeScript 中用于处理联合类型的条件类型,它会对联合类型的每个成员应用条件类型。
44. 什么是模板字面量类型?
模板字面量类型是 TypeScript 中用于基于字符串模板创建类型的方式,它可以用于创建字符串字面量类型的组合。
45. 什么是索引访问类型?
索引访问类型是 TypeScript 中用于访问对象属性类型的方式,它的形式为 T[K]。
46. 什么是映射类型修饰符?
映射类型修饰符是 TypeScript 中用于修改映射类型属性的修饰符,包括 readonly 和 ?。
47. 什么是类型参数约束?
类型参数约束是 TypeScript 中用于限制泛型类型范围的方式,它可以确保泛型类型具有某些特定的属性或方法。
48. 什么是类型断言函数?
类型断言函数是 TypeScript 中用于在运行时检查变量类型并断言其类型的函数,它返回一个类型谓词。
49. 什么是模块解析策略?
模块解析策略是 TypeScript 中用于查找导入模块的策略,包括 Node 和 Classic 两种策略。
50. 什么是三斜杠指令?
三斜杠指令是 TypeScript 中用于引用其他文件的指令,它的形式为 /// <reference path="..." />。
六、实践应用
51. 如何定义一个接口?
interface User {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
}52. 如何使用泛型?
function identity<T>(value: T): T {
return value;
}
const result = identity<string>('hello');53. 如何定义一个枚举?
enum Direction {
Up = 1,
Down,
Left,
Right
}
const direction = Direction.Up;54. 如何使用类型断言?
const value: unknown = 'hello';
const length = (value as string).length;
// 或
const length = (<string>value).length;55. 如何使用类型守卫?
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function processValue(value: unknown) {
if (isString(value)) {
// value 被推断为 string 类型
console.log(value.length);
}
}56. 如何使用可选链操作符?
interface User {
name: string;
address?: {
street?: string;
};
}
const user: User = { name: '张三' };
const street = user.address?.street;57. 如何使用空值合并操作符?
const value = null ?? 'default'; // 结果为 'default'
const value = undefined ?? 'default'; // 结果为 'default'
const value = '' ?? 'default'; // 结果为 ''
const value = 0 ?? 'default'; // 结果为 058. 如何使用类型别名?
type UserId = number;
type User = {
id: UserId;
name: string;
email: string;
};59. 如何使用交叉类型?
interface A {
a: string;
}
interface B {
b: number;
}
type C = A & B;
const c: C = { a: 'hello', b: 42 };60. 如何使用联合类型?
type Result = string | number | boolean;
function processResult(result: Result) {
if (typeof result === 'string') {
console.log(result.length);
} else if (typeof result === 'number') {
console.log(result.toFixed(2));
} else {
console.log(result);
}
}七、性能优化
61. TypeScript 编译性能优化的方法有哪些?
- 使用增量编译:
"incremental": true - 使用跳过库检查:
"skipLibCheck": true - 使用类型缓存:
"tsBuildInfoFile": ".tsbuildinfo" - 减少类型复杂度:避免过于复杂的类型定义
- 使用模块解析策略:选择合适的模块解析策略
62. 如何减少 TypeScript 的编译时间?
- 使用增量编译:
"incremental": true - 使用跳过库检查:
"skipLibCheck": true - 使用类型缓存:
"tsBuildInfoFile": ".tsbuildinfo" - 分割代码:将大型项目分割为多个子项目
- 使用 Webpack 或 Rollup:使用构建工具优化编译
63. 如何优化 TypeScript 的类型检查?
- 使用严格模式:
"strict": true - 使用 noImplicitAny:
"noImplicitAny": true - 使用 strictNullChecks:
"strictNullChecks": true - 使用 strictFunctionTypes:
"strictFunctionTypes": true - 使用 strictBindCallApply:
"strictBindCallApply": true - 使用 strictPropertyInitialization:
"strictPropertyInitialization": true
八、常见问题
64. 如何处理 null 和 undefined?
- 使用严格空检查:
"strictNullChecks": true - 使用可选链操作符:
obj?.prop - 使用空值合并操作符:
value ?? defaultValue - 使用类型守卫:
if (value !== null && value !== undefined)
65. 如何处理联合类型?
- 使用类型守卫:
if (typeof value === 'string') - 使用 switch 语句:根据类型执行不同的逻辑
- 使用类型断言:
(value as string).length
66. 如何处理泛型?
- 使用类型参数约束:
function generic<T extends string>(value: T) - 使用默认类型参数:
function generic<T = string>(value: T) - 使用泛型工具类型:
Partial<T>、Required<T>等
67. 如何处理接口和类型别名?
- 使用接口:定义对象类型,支持继承
- 使用类型别名:定义任意类型,包括联合类型、交叉类型等
68. 如何处理装饰器?
- 启用装饰器:
"experimentalDecorators": true - 使用装饰器:
@decorator class MyClass { } - 自定义装饰器:
function decorator(target: any) { }
69. 如何处理模块解析?
- 配置模块解析策略:
"moduleResolution": "node" - 使用相对路径:
import { foo } from './module' - 使用绝对路径:
import { foo } from '@/module'
70. 如何处理类型定义文件?
- 安装 @types 包:
npm install @types/node --save-dev - 创建自定义类型定义文件:
declarations.d.ts - 使用三斜杠指令:
/// <reference path="..." />
九、最佳实践
71. TypeScript 的最佳实践有哪些?
- 使用严格模式:
"strict": true - 使用有意义的类型名称:避免使用 any 类型
- 使用接口和类型别名:提高代码可读性
- 使用泛型:创建可重用的组件
- 使用类型守卫:提高类型安全性
- 使用可选链和空值合并:处理 null 和 undefined
- 使用 ESLint 和 Prettier:保持代码质量和风格
- 编写类型测试:确保类型定义正确
72. 如何编写可维护的 TypeScript 代码?
- 使用模块化:将代码分割为多个模块
- 使用命名约定:遵循一致的命名约定
- 使用注释:添加清晰的注释
- 使用类型定义:为所有变量和函数添加类型
- 使用接口:定义对象结构
- 使用泛型:创建可重用的组件
- 使用工具类型:简化类型定义
73. 如何调试 TypeScript 代码?
- 使用 source maps:
"sourceMap": true - 使用 VS Code 调试:设置断点和监视变量
- 使用 console.log:输出变量值
- 使用 TypeScript 编译器选项:
"noEmitOnError": true
74. 如何迁移 JavaScript 项目到 TypeScript?
- 安装 TypeScript:
npm install typescript --save-dev - 创建 tsconfig.json:
npx tsc --init - 重命名文件:将 .js 文件重命名为 .ts 文件
- 添加类型定义:为变量和函数添加类型
- 使用 any 类型:暂时使用 any 类型处理复杂类型
- 逐步迁移:逐个文件迁移,确保编译通过
75. 如何选择 TypeScript 的版本?
- 使用稳定版本:选择最新的稳定版本
- 考虑项目需求:根据项目的兼容性要求选择版本
- 使用 TypeScript ESLint:确保代码符合最新的 TypeScript 标准
- 关注 breaking changes:升级版本时注意 breaking changes
十、总结
TypeScript 是一种强大的静态类型语言,它为 JavaScript 添加了类型系统和其他特性,提高了代码的可维护性和可扩展性。通过掌握 TypeScript 的核心概念和最佳实践,你可以编写更安全、更可靠的代码,减少运行时错误,提高开发效率。
作为一名前端开发者,学习 TypeScript 将为你打开更多的职业机会,因为越来越多的企业和项目开始使用 TypeScript。TypeScript 的生态系统不断发展,社区活跃,是构建现代前端应用的理想选择。