Skip to content

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. 如何定义一个接口? ​

typescript
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // 可选属性
  readonly createdAt: Date; // 只读属性
}

52. 如何使用泛型? ​

typescript
function identity<T>(value: T): T {
  return value;
}

const result = identity<string>('hello');

53. 如何定义一个枚举? ​

typescript
enum Direction {
  Up = 1,
  Down,
  Left,
  Right
}

const direction = Direction.Up;

54. 如何使用类型断言? ​

typescript
const value: unknown = 'hello';
const length = (value as string).length;
// 或
const length = (<string>value).length;

55. 如何使用类型守卫? ​

typescript
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. 如何使用可选链操作符? ​

typescript
interface User {
  name: string;
  address?: {
    street?: string;
  };
}

const user: User = { name: '张三' };
const street = user.address?.street;

57. 如何使用空值合并操作符? ​

typescript
const value = null ?? 'default'; // 结果为 'default'
const value = undefined ?? 'default'; // 结果为 'default'
const value = '' ?? 'default'; // 结果为 ''
const value = 0 ?? 'default'; // 结果为 0

58. 如何使用类型别名? ​

typescript
type UserId = number;
type User = {
  id: UserId;
  name: string;
  email: string;
};

59. 如何使用交叉类型? ​

typescript
interface A {
  a: string;
}

interface B {
  b: number;
}

type C = A & B;
const c: C = { a: 'hello', b: 42 };

60. 如何使用联合类型? ​

typescript
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 的生态系统不断发展,社区活跃,是构建现代前端应用的理想选择。

Released under the MIT License.