《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [61. 什么是泛型?它解决了什么问题?举例说明其核心价值。](#61. 什么是泛型?它解决了什么问题?举例说明其核心价值。)
- [62. 如何定义泛型函数?如何指定泛型的默认类型?](#62. 如何定义泛型函数?如何指定泛型的默认类型?)
- [63. 如何定义泛型接口和泛型类?](#63. 如何定义泛型接口和泛型类?)
- [64. 什么是泛型约束?如何用`extends`实现泛型约束?举例说明。](#64. 什么是泛型约束?如何用
extends
实现泛型约束?举例说明。) - [65. 如何对泛型参数进行"多约束"(即同时满足多个条件)?](#65. 如何对泛型参数进行“多约束”(即同时满足多个条件)?)
- [66. `keyof`操作符的作用是什么?如何结合泛型使用?](#66.
keyof
操作符的作用是什么?如何结合泛型使用?) - [67. 泛型工具类型`Partial`、`Required`、`Pick`、`Omit`的作用是什么?如何实现一个简易版`Partial`?](#67. 泛型工具类型
Partial
、Required
、Pick
、Omit
的作用是什么?如何实现一个简易版Partial
?) - [68. 什么是泛型的"协变"和"逆变"?`in`关键字如何影响泛型的变异性?](#68. 什么是泛型的“协变”和“逆变”?
in
关键字如何影响泛型的变异性?) - [69. 如何用泛型实现一个"深拷贝"函数的类型定义?](#69. 如何用泛型实现一个“深拷贝”函数的类型定义?)
- [70. 泛型与联合类型结合时,如何避免"分布式条件类型"的副作用?](#70. 泛型与联合类型结合时,如何避免“分布式条件类型”的副作用?)
-
- 二、100道TypeScript面试题目录列表
一、本文面试题目录
61. 什么是泛型?它解决了什么问题?举例说明其核心价值。
-
原理说明 :
泛型(Generics)是 TypeScript 中用于创建可复用、类型安全的组件的工具,它允许在定义函数、接口或类时不预先指定具体类型,而是在使用时动态指定。
它解决的核心问题是:代码复用与类型约束的矛盾 。没有泛型时,要么通过
any
牺牲类型安全以实现复用,要么为每种类型重复编写逻辑。泛型通过"类型参数"实现了"同一份代码适配多种类型",同时保持类型检查。 -
示例代码:
typescript// 不使用泛型:要么丢失类型检查(any),要么重复实现 function identityAny(arg: any): any { return arg; } const num1: number = identityAny(123); // 实际类型为any,无类型约束 // 使用泛型:复用逻辑且保留类型 function identity<T>(arg: T): T { return arg; } const num2: number = identity(123); // 正确,T被推断为number const str: string = identity("hello"); // 正确,T被推断为string
-
核心价值:
- 类型安全:避免
any
导致的类型丢失。 - 代码复用:一份逻辑适配多种类型(如通用工具函数、容器类)。
- 灵活性:使用时动态指定类型,兼顾扩展性与约束性。
- 类型安全:避免
62. 如何定义泛型函数?如何指定泛型的默认类型?
-
原理说明 :
泛型函数通过在函数名后添加
<类型参数>
定义,类型参数可在参数、返回值中使用。泛型默认类型允许为类型参数指定默认值,当未显式或隐式指定类型时,使用默认类型。
-
示例代码:
typescript// 1. 基础泛型函数定义 function logAndReturn<T>(value: T): T { console.log(value); return value; } logAndReturn<number>(123); // 显式指定T为number logAndReturn("hello"); // 隐式推断T为string // 2. 泛型默认类型(使用 = 指定) function createArray<T = string>(length: number, value: T): T[] { return Array(length).fill(value); } createArray(3, "a"); // T默认为string,返回string[] createArray<number>(3, 0); // 显式指定T为number,返回number[]
63. 如何定义泛型接口和泛型类?
-
原理说明 :
泛型接口和类通过在名称后添加
<类型参数>
定义,类型参数可用于约束接口的属性/方法或类的成员。 -
示例代码:
typescript// 1. 泛型接口 interface Container<T> { value: T; setValue: (v: T) => void; } // 使用时指定类型 const numContainer: Container<number> = { value: 0, setValue: (v) => numContainer.value = v }; // 2. 泛型类 class Stack<T> { private items: T[] = []; push(item: T): void { this.items.push(item); } pop(): T | undefined { return this.items.pop(); } } const stringStack = new Stack<string>(); stringStack.push("a"); // 正确 stringStack.push(123); // 错误:类型number不符合string
64. 什么是泛型约束?如何用extends
实现泛型约束?举例说明。
-
原理说明 :
泛型约束(Generic Constraints)用于限制类型参数的范围,确保其包含特定属性或方法,避免在使用泛型时访问不存在的成员。通过
extends
关键字实现,指定类型参数必须满足的条件。 -
示例代码:
typescript// 约束T必须包含length属性 interface HasLength { length: number; } // T extends HasLength:确保T有length属性 function getLength<T extends HasLength>(arg: T): number { return arg.length; // 安全访问length } getLength("hello"); // 正确:string有length getLength([1, 2, 3]); // 正确:数组有length getLength(123); // 错误:number无length属性
65. 如何对泛型参数进行"多约束"(即同时满足多个条件)?
-
原理说明 :
多约束指类型参数需同时满足多个条件,可通过
extends
结合交叉类型(&
)实现,即T extends A & B & C
表示 T 必须同时符合 A、B、C 的约束。 -
示例代码:
typescriptinterface HasId { id: number; } interface HasName { name: string; } // T必须同时满足HasId和HasName function logEntity<T extends HasId & HasName>(entity: T): void { console.log(`ID: ${entity.id}, Name: ${entity.name}`); } logEntity({ id: 1, name: "test" }); // 正确:满足两个约束 logEntity({ id: 1 }); // 错误:缺少name(不满足HasName) logEntity({ name: "test" }); // 错误:缺少id(不满足HasId)
66. keyof
操作符的作用是什么?如何结合泛型使用?
-
原理说明 :
keyof
用于获取某个类型的所有键名,返回一个由键名组成的联合类型。结合泛型时,可动态约束参数为对象的键,实现类型安全的属性访问。 -
示例代码:
typescript// 1. keyof基础用法 interface User { name: string; age: number; } type UserKeys = keyof User; // 等价于 "name" | "age" // 2. 结合泛型:安全访问对象属性 function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; // 确保key是T的有效键 } const user: User = { name: "Alice", age: 30 }; getProperty(user, "name"); // 正确:返回string getProperty(user, "age"); // 正确:返回number getProperty(user, "email"); // 错误:"email"不是User的键
67. 泛型工具类型Partial
、Required
、Pick
、Omit
的作用是什么?如何实现一个简易版Partial
?
-
原理说明 :
泛型工具类型是 TypeScript 内置的基于泛型的类型转换工具,用于快速生成新类型:
Partial<T>
:将 T 的所有属性转为可选。Required<T>
:将 T 的所有属性转为必填(与Partial
相反)。Pick<T, K>
:从 T 中选取键为 K 的属性组成新类型。Omit<T, K>
:从 T 中排除键为 K 的属性组成新类型(与Pick
相反)。
-
示例代码:
typescriptinterface User { id: number; name: string; age: number; } // 内置工具类型示例 type PartialUser = Partial<User>; // { id?: number; name?: string; age?: number } type RequiredUser = Required<PartialUser>; // 恢复为User(所有属性必填) type UserName = Pick<User, "name">; // { name: string } type UserWithoutAge = Omit<User, "age">; // { id: number; name: string } // 简易版Partial实现 type MyPartial<T> = { [P in keyof T]?: T[P]; // 遍历T的所有键,转为可选 }; type MyPartialUser = MyPartial<User>; // 效果同Partial<User>
68. 什么是泛型的"协变"和"逆变"?in
关键字如何影响泛型的变异性?
-
原理说明 :
泛型的"变异性"描述类型参数的子类型关系如何影响泛型类型的子类型关系:
- 协变(Covariant) :若
A extends B
,则Generic<A> extends Generic<B>
(子类型关系保留)。 - 逆变(Contravariant) :若
A extends B
,则Generic<B> extends Generic<A>
(子类型关系反转)。 - TypeScript 中,默认情况下:
- 泛型接口/类的属性是协变的。
- 函数参数是逆变的(在
strictFunctionTypes
开启时)。
in
关键字用于标记泛型参数为"逆变位置"(通常用于函数参数),强制其逆变行为。
- 协变(Covariant) :若
-
示例代码:
typescript// 协变示例(数组是协变的) type Animal = { name: string }; type Dog = Animal & { bark: () => void }; const dogs: Dog[] = [{ name: "Buddy", bark: () => {} }]; const animals: Animal[] = dogs; // 正确:Dog[] 是 Animal[] 的子类型(协变) // 逆变示例(函数参数是逆变的) type AnimalHandler = (a: Animal) => void; type DogHandler = (d: Dog) => void; const animalHandler: AnimalHandler = (a) => console.log(a.name); const dogHandler: DogHandler = (d) => d.bark(); // 当strictFunctionTypes开启时: const handler: DogHandler = animalHandler; // 正确:AnimalHandler 是 DogHandler 的子类型(逆变) const handler2: AnimalHandler = dogHandler; // 错误:DogHandler 不是 AnimalHandler 的子类型
69. 如何用泛型实现一个"深拷贝"函数的类型定义?
-
原理说明 :
深拷贝函数需递归复制对象的所有层级,类型定义需通过泛型递归处理嵌套结构,区分基本类型、数组、对象等。
-
示例代码:
typescripttype DeepClone<T> = T extends number | string | boolean | null | undefined | symbol | bigint ? T // 基本类型直接返回 : T extends Array<infer U> ? Array<DeepClone<U>> // 数组:递归处理元素 : { [K in keyof T]: DeepClone<T[K]> }; // 对象:递归处理属性 function deepClone<T>(value: T): DeepClone<T> { if (typeof value !== "object" || value === null) { return value as DeepClone<T>; } if (Array.isArray(value)) { return value.map(deepClone) as DeepClone<T>; } const cloned: Record<string, any> = {}; for (const key in value) { cloned[key] = deepClone(value[key]); } return cloned as DeepClone<T>; } // 测试 const obj = { a: 1, b: { c: "hello" }, d: [1, 2] }; const cloned = deepClone(obj); // cloned类型:{ a: number; b: { c: string }; d: number[] }(与原对象类型一致)
70. 泛型与联合类型结合时,如何避免"分布式条件类型"的副作用?
-
原理说明 :
分布式条件类型指:当泛型参数为联合类型
A | B | C
时,条件类型T extends U ? X : Y
会自动分发为(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
。若需避免这种分发行为,可通过将泛型参数包裹在元组中([T] extends [U]
)阻止分布式处理。 -
示例代码:
typescript// 分布式条件类型(默认行为) type Distributed<T> = T extends string ? "string" : "other"; type Result1 = Distributed<string | number>; // "string" | "other"(分发处理) // 避免分布式行为(用元组包裹) type NonDistributed<T> = [T] extends [string] ? "string" : "other"; type Result2 = NonDistributed<string | number>; // "other"(整体判断,不分发) // 应用场景:判断类型是否完全匹配联合类型 type IsExactStringUnion<T> = [T] extends [string] ? true : false; type Test1 = IsExactStringUnion<string>; // true type Test2 = IsExactStringUnion<string | number>; // false(避免了分发导致的错误判断)