Skip to content

TypeScript 面试题

1. 说说对 TypeScript 中命名空间与模块的理解?区别?

答:

  • 命名空间 (Namespace):用于组织代码,避免全局命名冲突,使用 namespace 关键字声明,编译后会生成嵌套的对象结构
  • 模块 (Module):基于文件的组织方式,每个文件就是一个模块,使用 import/export 进行导入导出,是 ES6 模块系统的实现

区别

特性命名空间模块
组织方式逻辑分组文件级别
导出方式export 内部文件级 export
导入方式/// <reference>`` 或 importimport
使用场景全局库、内部组织现代应用开发
推荐度不推荐(遗留方案)推荐

2. 说说你对 TypeScript 的理解?与 JavaScript 的区别?

答:

TypeScript 是 JavaScript 的超集,添加了静态类型系统。

主要区别

特性JavaScriptTypeScript
类型系统动态类型静态类型
编译运行时解释编译为 JS
工具支持有限更好的 IDE 支持、智能提示
新特性支持依赖浏览器支持可编译为低版本 JS
学习曲线需要学习类型系统
代码可读性运行时才知道类型声明清晰,易于维护

3. TypeScript 中泛型是什么?

答:

泛型 (Generics) 允许创建可重用的组件,支持多种类型。

typescript
// 基本用法
function identity`<T>`(arg: T): T {
  return arg;
}

// 泛型约束
interface Lengthwise {
  length: number;
}
function loggingIdentity`<T extends Lengthwise>`(arg: T): T {
  console.log(arg.length);
  return arg;
}

// 泛型接口
interface GenericResponse`<T>` {
  data: T;
  status: number;
}

4. TypeScript 中有哪些声明变量的方式?

答:

方式说明示例
let块级作用域,可重新赋值let x = 1;
const块级作用域,不可重新赋值const y = 2;
var函数作用域(不推荐)var z = 3;
类型注解显式声明类型let a: number = 1;
类型推断自动推断类型let b = "hello"; // string
联合类型多个类型之一`let c: string
类型别名自定义类型名`type ID = string

5. 什么是 TypeScript 的方法重载?

答:

方法重载允许一个函数有多个签名,根据参数类型或数量执行不同逻辑。

typescript
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
  return a + b;
}

add(1, 2);        // 返回 number
add("a", "b");    // 返回 string

6. 请实现下面的 sleep 方法

答:

typescript
function sleep(ms: number): Promise`<void>` {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
async function demo() {
  console.log('开始');
  await sleep(1000);
  console.log('1秒后');
}

7. TypeScript 中的 is 关键字有什么用?

答:

is 是类型谓词,用于自定义类型保护函数。

typescript
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function process(value: string | number) {
  if (isString(value)) {
    // TypeScript 知道这里是 string 类型
    return value.toUpperCase();
  }
  // 这里是 number 类型
  return value.toFixed(2);
}

8. TypeScript 支持的访问修饰符有哪些?

答:

修饰符说明访问范围
public公开(默认)类内部、子类、实例
private私有仅类内部
protected受保护类内部、子类
readonly只读只能在声明或构造函数中赋值
typescript
class Animal {
  public name: string;
  private age: number;
  protected type: string;
  readonly id: number;
}

9. 请实现下面的 myMap 方法

答:

typescript
function myMap`<T, U>`(array: T[], callback: (item: T, index: number, arr: T[]) => U): U[] {
  const result: U[] = [];
  for (let i = 0; i < array.length; i++) {
    result.push(callback(array[i], i, array));
  }
  return result;
}

// 使用
const nums = [1, 2, 3];
const doubled = myMap(nums, n => n * 2); // [2, 4, 6]

10. 请实现下面的 treePath 方法

答:

typescript
interface TreeNode {
  id: string;
  children?: TreeNode[];
}

function treePath(tree: TreeNode[], targetId: string, path: string[] = []): string[] | null {
  for (const node of tree) {
    const currentPath = [...path, node.id];
    if (node.id === targetId) {
      return currentPath;
    }
    if (node.children) {
      const result = treePath(node.children, targetId, currentPath);
      if (result) return result;
    }
  }
  return null;
}

11. 请实现下面的 product 方法

答:

typescript
// 计算数组中所有元素的乘积
function product(nums: number[]): number {
  return nums.reduce((acc, curr) => acc * curr, 1);
}

// 或处理空数组情况
function productSafe(nums: number[]): number | undefined {
  if (nums.length === 0) return undefined;
  return nums.reduce((acc, curr) => acc * curr, 1);
}

12. 请实现下面的 myAll 方法(Promise.all)

答:

typescript
function myAll`<T>`(promises: Promise`<T>`[]): Promise`<T[]>` {
  return new Promise((resolve, reject) => {
    if (promises.length === 0) {
      resolve([]);
      return;
    }
    
    const results: T[] = new Array(promises.length);
    let completedCount = 0;
    
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          results[index] = value;
          completedCount++;
          if (completedCount === promises.length) {
            resolve(results);
          }
        })
        .catch(reject);
    });
  });
}

13. 请实现下面的 sum 方法

答:

typescript
// 支持多个参数求和
function sum(...numbers: number[]): number {
  return numbers.reduce((acc, curr) => acc + curr, 0);
}

// 或柯里化版本
function sumCurry(a: number): (b: number) => number {
  return (b: number) => a + b;
}

14. 请实现下面的 mergeArray 方法

答:

typescript
// 合并两个数组,去重
function mergeArray`<T>`(arr1: T[], arr2: T[]): T[] {
  return [...new Set([...arr1, ...arr2])];
}

// 或根据某个 key 合并对象数组
function mergeArrayByKey`<T extends Record<string, any>`>(
  arr1: T[], 
  arr2: T[], 
  key: keyof T
): T[] {
  const map = new Map`<string, T>`();
  [...arr1, ...arr2].forEach(item => {
    map.set(String(item[key]), item);
  });
  return Array.from(map.values());
}

15. 实现下面的 firstSingleChar 方法

答:

typescript
// 找到字符串中第一个不重复的字符
function firstSingleChar(str: string): string | null {
  const charCount = new Map`<string, number>`();
  
  // 统计字符出现次数
  for (const char of str) {
    charCount.set(char, (charCount.get(char) || 0) + 1);
  }
  
  // 找到第一个只出现一次的字符
  for (const char of str) {
    if (charCount.get(char) === 1) {
      return char;
    }
  }
  
  return null;
}

16. 实现下面的 reverseWord 方法

答:

typescript
// 反转字符串中的每个单词
function reverseWord(str: string): string {
  return str
    .split(' ')
    .map(word => word.split('').reverse().join(''))
    .join(' ');
}

// 示例
reverseWord("hello world"); // "olleh dlrow"

17. 如何定义一个数组,它的元素可能是字符串类型,也可能是数值类型?

答:

typescript
// 联合类型
let arr: (string | number)[] = [1, "hello", 2, "world"];

// 或使用类型别名
type StringOrNumber = string | number;
let arr2: StringOrNumber[] = [1, "hello"];

18. 请补充 objToArray 函数

答:

typescript
interface ObjToArrayResult {
  key: string;
  value: any;
}

function objToArray(obj: Record`<string, any>`): ObjToArrayResult[] {
  return Object.entries(obj).map(([key, value]) => ({ key, value }));
}

// 示例
objToArray({ a: 1, b: 2 }); // [{ key: "a", value: 1 }, { key: "b", value: 2 }]

19. 使用 TS 实现一个判断传入参数是否是数组类型的方法

答:

typescript
function isArray(value: unknown): value is any[] {
  return Array.isArray(value);
}

// 或更精确的泛型版本
function isArrayOfType`<T>`(
  value: unknown, 
  typeCheck: (item: unknown) => item is T
): value is T[] {
  return Array.isArray(value) && value.every(typeCheck);
}

// 使用
function isStringArray(value: unknown): value is string[] {
  return isArrayOfType(value, (item): item is string => typeof item === 'string');
}

20. TypeScript 的内置数据类型有哪些?

答:

类型说明
number数字(整数和浮点数)
string字符串
boolean布尔值
null空值
undefined未定义
symbol唯一标识符
bigint大整数
object对象类型
any任意类型
unknown未知类型(安全的 any)
never永不存在的类型
void无返回值
array数组
tuple元组
enum枚举

21. ts 中 any 和 unknown 有什么区别?

答:

特性anyunknown
类型安全不安全,可以任意操作安全,操作前需要类型检查
赋值可赋给任何类型只能赋给 unknownany
属性访问可直接访问任意属性不能直接访问,需要类型断言或收窄
使用场景遗留代码、快速原型推荐使用,表示不确定类型
typescript
let a: any = 1;
a.toFixed(); // 编译通过,运行可能报错

let u: unknown = 1;
// u.toFixed(); // 编译错误!
if (typeof u === 'number') {
  u.toFixed(); // 正确
}

22. 如何将 unknown 类型指定为一个更具体的类型?

答:

typescript
let value: unknown = "hello";

// 方法1:类型守卫(Type Guards)
if (typeof value === 'string') {
  console.log(value.toUpperCase()); // string 类型
}

// 方法2:类型断言(Type Assertion)
const str = value as string;

// 方法3:类型谓词(Type Predicate)
function isString(val: unknown): val is string {
  return typeof val === 'string';
}
if (isString(value)) {
  console.log(value.length);
}

// 方法4:switch 收窄
type Animal = { kind: 'dog'; woof: () => void } | { kind: 'cat'; meow: () => void };
function handleAnimal(animal: unknown) {
  if (typeof animal === 'object' && animal !== null && 'kind' in animal) {
    const a = animal as Animal;
    switch (a.kind) {
      case 'dog': a.woof(); break;
      case 'cat': a.meow(); break;
    }
  }
}

Released under the MIT License.