Appearance
TypeScript 经典面试题 100 道
按重要程度由高到低排序,每道题均包含详细答案和可运行案例
第1-10题:核心基础(最重要)
1. TypeScript 是什么?与 JavaScript 的区别?
答案:
TypeScript 是 JavaScript 的超集,添加了静态类型系统。
核心区别:
| 特性 | JavaScript | TypeScript |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型 |
| 编译 | 解释执行 | 需要编译为 JS |
| 错误发现 | 运行时 | 编译时 |
| IDE支持 | 有限 | 强大的智能提示 |
| 代码可读性 | 依赖注释 | 类型即文档 |
案例:
typescript
// JavaScript - 运行时才发现错误
function add(a, b) {
return a + b
}
add(1, '2') // "12" - 无错误提示,但结果可能不符合预期
// TypeScript - 编译时就发现错误
function addTS(a: number, b: number): number {
return a + b
}
// addTS(1, "2"); // ❌ 编译错误:Argument of type 'string' is not assignable to parameter of type 'number'2. 基本类型有哪些?如何声明?
答案:
TypeScript 的基本类型包括:string、number、boolean、null、undefined、symbol、bigint、any、unknown、never、void、object。
案例:
typescript
// 原始类型
let str: string = 'hello'
let num: number = 42
let bool: boolean = true
let n: null = null
let u: undefined = undefined
let sym: symbol = Symbol('key')
let big: bigint = 100n
// 特殊类型
let anyType: any = 'anything' // 任意类型,慎用
let unknownType: unknown = 'unknown' // 类型安全的any
let neverType: never // 永不返回的类型
let voidType: void = undefined // 无返回值
// 数组和元组
let arr: number[] = [1, 2, 3]
let tuple: [string, number] = ['age', 25]
// 对象
let obj: { name: string; age: number } = { name: 'Tom', age: 25 }3. any、unknown、never、void 之间的对比?
答案:
| 类型 | 描述 | 特点 | 安全性 |
|---|---|---|---|
any | 任意类型,完全放弃类型检查 | 可赋值给任何类型,可调用任何方法 | 不安全 |
unknown | 类型安全的 any | 赋值给其他类型前必须类型检查 | 安全 |
never | 永不存在的值的类型 | 无值,不能赋值给任何类型(除了 never) | 安全 |
void | 无返回值或返回值被忽略 | 通常用于函数返回类型 | 安全 |
案例:
typescript
// any - 不安全
let anyValue: any = 'hello'
anyValue.toFixed() // ❌ 运行时错误,但编译不报错
let num1: number = anyValue // ✅ 可以赋值给任何类型
// unknown - 安全
let unknownValue: unknown = 'hello'
// unknownValue.toFixed(); // ❌ 编译错误:Object is of type 'unknown'
// let num2: number = unknownValue; // ❌ 编译错误
// 必须先进行类型检查
if (typeof unknownValue === 'string') {
console.log(unknownValue.toUpperCase()) // ✅ 安全
}
// never - 永不返回
function throwError(message: string): never {
throw new Error(message)
}
function infiniteLoop(): never {
while (true) {}
}
// void - 无返回值
function logMessage(message: string): void {
console.log(message)
// 没有 return 语句或 return undefined
}
// 类型兼容性
let anyValue2: any = 'anything'
let neverValue: never
let voidValue: void = undefined
anyValue2 = neverValue // ✅ never 可以赋值给任何类型
// neverValue = anyValue2; // ❌ 任何类型都不能赋值给 never
voidValue = undefined // ✅
// let num: number = voidValue; // ❌ void 不能赋值给其他类型4. 什么是类型推断?
答案:
类型推断是 TypeScript 编译器自动推断变量类型的能力,无需显式声明。
案例:
typescript
// 基础类型推断
let message = 'Hello' // 推断为 string
let count = 42 // 推断为 number
let isActive = true // 推断为 boolean
// 函数返回值推断
function add(a: number, b: number) {
return a + b // 推断返回 number
}
// 上下文推断
const numbers = [1, 2, 3] // 推断为 number[]
numbers.push('4') // ❌ 错误:Argument of type 'string' is not assignable to parameter of type 'number'
// 最佳通用类型推断
let arr = [0, 1, null] // 推断为 (number | null)[]
// 对象推断
let person = {
name: 'Tom',
age: 25
} // 推断为 { name: string; age: number }5. 什么是类型断言?如何使用?
答案:
类型断言告诉编译器某个值的具体类型,类似于类型转换,但只在编译时起作用。
语法:
- 尖括号语法:
<Type>value - as 语法:
value as Type(推荐,JSX 中必须使用)
案例:
typescript
// 基础类型断言
let someValue: unknown = 'this is a string'
let strLength1 = (<string>someValue).length
let strLength2 = (someValue as string).length
// DOM 元素断言
const input = document.getElementById('myInput') as HTMLInputElement
input.value = 'hello' // 可以访问 value 属性
// 双重断言(谨慎使用)
let num = 'hello' as unknown as number
// 非空断言(!)
function getName(): string | null {
return 'Tom'
}
let name = getName()! // 告诉编译器值一定不为 null/undefined
console.log(name.toUpperCase())
// const 断言
let arr = [1, 2, 3] as const // 变为 readonly [1, 2, 3]
let obj = { x: 10, y: 20 } as const // 所有属性变为 readonly
// satisfies 关键字(TS 4.9+)
const config = {
host: 'localhost',
port: 3000
} satisfies { host: string; port: number }6. 接口(Interface)和类型别名(Type Alias)的区别?
答案:
| 特性 | Interface | Type Alias |
|---|---|---|
| 扩展方式 | extends | &(交叉类型) |
| 同名合并 | ✅ 支持声明合并 | ❌ 不支持 |
| 适用范围 | 仅对象类型 | 任意类型 |
| 实现类 | ✅ 可以被类实现 | ❌ 不能被类实现 |
| 性能 | 稍快(缓存) | 稍慢 |
案例:
typescript
// Interface - 支持声明合并
interface Person {
name: string
}
interface Person {
age: number
}
// 合并后: { name: string; age: number }
const person: Person = { name: 'Tom', age: 25 }
// Interface 扩展
interface Employee extends Person {
salary: number
}
// Type Alias - 更灵活
type ID = string | number
type Point = { x: number; y: number }
type Point3D = Point & { z: number } // 交叉类型扩展
// Type Alias 用于联合类型
type Status = 'pending' | 'success' | 'error'
type Nullable<T> = T | null
// 类实现接口
interface Printable {
print(): void
}
class Document implements Printable {
print() {
console.log('Printing document...')
}
}7. 联合类型(Union Types)和交叉类型(Intersection Types)
答案:
- 联合类型(|):值可以是多种类型之一
- 交叉类型(&):将多个类型合并为一个类型
案例:
typescript
// 联合类型
function printId(id: number | string): void {
if (typeof id === 'string') {
console.log(id.toUpperCase())
} else {
console.log(id.toFixed(2))
}
}
printId(101)
printId('abc')
// 字面量联合类型
type Status = 'pending' | 'success' | 'error'
type Priority = 1 | 2 | 3 | 4 | 5
function setStatus(status: Status): void {
console.log(`Status: ${status}`)
}
setStatus('success') // ✅
// setStatus("loading"); // ❌ 错误
// 交叉类型
interface Person {
name: string
}
interface Employee {
employeeId: number
}
type EmployeePerson = Person & Employee
const employee: EmployeePerson = {
name: 'Tom',
employeeId: 123
}
// 复杂交叉类型
interface Loggable {
log(): void
}
interface Serializable {
serialize(): string
}
type LoggableAndSerializable = Loggable & Serializable
class MyClass implements LoggableAndSerializable {
log() {
console.log('Logging...')
}
serialize() {
return JSON.stringify(this)
}
}
// 联合类型的类型保护
interface Bird {
fly(): void
layEggs(): void
}
interface Fish {
swim(): void
layEggs(): void
}
type Animal = Bird | Fish
function moveAnimal(animal: Animal): void {
if ('fly' in animal) {
animal.fly() // TypeScript 知道这是 Bird
} else {
animal.swim() // TypeScript 知道这是 Fish
}
animal.layEggs() // 两者都有
}8. 什么是泛型?如何使用?
答案:
泛型(Generics)允许定义可重用的组件,支持多种类型,同时保持类型安全。
案例:
typescript
// 基础泛型函数
function identity<T>(arg: T): T {
return arg
}
let output1 = identity<string>('hello')
let output2 = identity(42) // 类型推断
// 泛型约束
interface HasLength {
length: number
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length)
return arg
}
logLength('hello') // ✅
logLength([1, 2, 3]) // ✅
// logLength(42); // ❌ number 没有 length 属性
// 泛型接口
interface GenericResponse<T> {
data: T
status: number
message: string
}
const userResponse: GenericResponse<{ id: number; name: string }> = {
data: { id: 1, name: 'Tom' },
status: 200,
message: 'Success'
}
// 泛型类
class Queue<T> {
private data: T[] = []
push(item: T) {
this.data.push(item)
}
pop(): T | undefined {
return this.data.shift()
}
}
const numberQueue = new Queue<number>()
numberQueue.push(10)
// numberQueue.push("hello"); // ❌ 错误
// 泛型默认值
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value)
}9. 可选参数、默认参数和剩余参数
答案:
TypeScript 支持多种函数参数形式,增强函数的灵活性。
案例:
typescript
// 可选参数(?)
function greet(name: string, greeting?: string): string {
if (greeting) {
return `${greeting}, ${name}!`
}
return `Hello, ${name}!`
}
console.log(greet('Tom')) // "Hello, Tom!"
console.log(greet('Tom', 'Hi')) // "Hi, Tom!"
// 默认参数
function greetWithDefault(name: string, greeting: string = 'Hello'): string {
return `${greeting}, ${name}!`
}
// 剩余参数(...)
function sum(...numbers: number[]): number {
return numbers.reduce((total, num) => total + num, 0)
}
console.log(sum(1, 2, 3, 4)) // 10
// 解构参数
function printUser({ name, age }: { name: string; age: number }): void {
console.log(`${name} is ${age} years old`)
}
printUser({ name: 'Tom', age: 25 })
// 可选参数必须在必需参数之后
function example(a: number, b?: number, c: number = 10): number {
return a + (b ?? 0) + c
}10. 枚举(Enum)类型详解
答案:
枚举用于定义一组命名的常量,可以是数字枚举或字符串枚举。
案例:
typescript
// 数字枚举(默认从0开始)
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 指定起始值
enum Status {
Pending = 1,
Processing, // 2
Completed, // 3
Failed // 4
}
// 字符串枚举
enum Color {
Red = 'RED',
Green = 'GREEN',
Blue = 'BLUE'
}
// 异构枚举(不推荐)
enum Mixed {
No = 0,
Yes = 'YES'
}
// 使用
function move(direction: Direction): void {
console.log(`Moving ${Direction[direction]}`)
}
move(Direction.Up) // "Moving Up"
// 常量枚举(编译时内联,性能更好)
const enum Permission {
Read = 1,
Write = 2,
Execute = 4
}
const canWrite = Permission.Write // 编译后直接替换为 2
// 反向映射(仅数字枚举)
console.log(Direction.Up) // 0
console.log(Direction[0]) // "Up"
// 枚举作为类型
function setColor(color: Color): void {
console.log(`Setting color to ${color}`)
}
setColor(Color.Red) // ✅
// setColor("RED"); // ❌ 错误第11-25题:类型系统进阶
11. 类型保护(Type Guards)有哪些方式?
答案:
类型保护用于在运行时检查类型,帮助 TypeScript 缩小类型范围。
案例:
typescript
// 1. typeof 类型保护
function padLeft(value: string, padding: string | number): string {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value
}
return padding + value
}
// 2. instanceof 类型保护
class Dog {
bark() {
console.log('Woof!')
}
}
class Cat {
meow() {
console.log('Meow!')
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark()
} else {
animal.meow()
}
}
// 3. in 操作符类型保护
interface Car {
drive(): void
wheels: number
}
interface Boat {
sail(): void
draft: number
}
function operate(vehicle: Car | Boat): void {
if ('drive' in vehicle) {
vehicle.drive()
} else {
vehicle.sail()
}
}
// 4. 自定义类型保护函数
interface Fish {
swim(): void
}
interface Bird {
fly(): void
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined
}
function move(pet: Fish | Bird): void {
if (isFish(pet)) {
pet.swim()
} else {
pet.fly()
}
}
// 5. 字面量类型保护
type Shape =
| { kind: 'circle'; radius: number }
| { kind: 'square'; side: number }
| { kind: 'rectangle'; width: number; height: number }
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'square':
return shape.side ** 2
case 'rectangle':
return shape.width * shape.height
default:
// 穷尽性检查
const _exhaustiveCheck: never = shape
return _exhaustiveCheck
}
}12. 什么是类型谓词(Type Predicates)?
答案:
类型谓词是一种特殊的返回类型,用于自定义类型保护函数,格式为 parameterName is Type。
案例:
typescript
// 基础类型谓词
interface Cat {
name: string
meow(): void
}
interface Dog {
name: string
bark(): void
}
type Pet = Cat | Dog
// 类型谓词函数
function isCat(pet: Pet): pet is Cat {
return (pet as Cat).meow !== undefined
}
function greetPet(pet: Pet): void {
if (isCat(pet)) {
pet.meow() // TypeScript 知道这是 Cat
} else {
pet.bark() // TypeScript 知道这是 Dog
}
}
// 更复杂的类型谓词
interface Admin {
role: 'admin'
permissions: string[]
}
interface User {
role: 'user'
email: string
}
type Person = Admin | User
function isAdmin(person: Person): person is Admin {
return person.role === 'admin'
}
function checkAccess(person: Person): void {
if (isAdmin(person)) {
console.log('Admin permissions:', person.permissions)
} else {
console.log('User email:', person.email)
}
}
// 数组过滤中的类型谓词
const pets: Pet[] = [
{ name: 'Whiskers', meow() {} },
{ name: 'Rex', bark() {} },
{ name: 'Mittens', meow() {} }
]
// 使用类型谓词过滤
const cats = pets.filter(isCat) // Cat[]
const dogs = pets.filter((pet): pet is Dog => !isCat(pet)) // Dog[]
// 泛型类型谓词
function isDefined<T>(value: T | undefined | null): value is T {
return value !== undefined && value !== null
}
const values = [1, null, 2, undefined, 3]
const definedValues = values.filter(isDefined) // number[]13. 索引签名(Index Signatures)是什么?
答案:
索引签名允许定义对象可以具有任意数量的属性,这些属性的键和值类型遵循特定模式。
案例:
typescript
// 字符串索引签名
interface StringDictionary {
[key: string]: string
}
const dict: StringDictionary = {
name: 'Tom',
email: 'tom@example.com'
}
// 数字索引签名
interface NumberArray {
[index: number]: string
}
const arr: NumberArray = ['a', 'b', 'c']
// 混合索引签名
interface Mixed {
[key: string]: string | number
name: string // 必须兼容索引签名类型
age: number
}
// 只读索引签名
interface ReadonlyDictionary {
readonly [key: string]: number
}
const readonlyDict: ReadonlyDictionary = { a: 1, b: 2 }
// readonlyDict.a = 3; // ❌ 错误:只读属性
// 实用案例:计数器
interface WordCount {
[word: string]: number
}
function countWords(text: string): WordCount {
const counts: WordCount = {}
const words = text.toLowerCase().split(/\s+/)
for (const word of words) {
counts[word] = (counts[word] || 0) + 1
}
return counts
}
console.log(countWords('hello world hello')) // { hello: 2, world: 1 }
// 索引签名与 Record 工具类型
type PageInfo = {
title: string
}
type Pages = Record<string, PageInfo>
// 等价于:{ [key: string]: PageInfo }
const pages: Pages = {
home: { title: 'Home' },
about: { title: 'About' }
}14. 映射类型(Mapped Types)详解
答案:
映射类型基于旧类型创建新类型,通过遍历属性键来转换每个属性。
案例:
typescript
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
type Required<T> = {
[P in keyof T]-?: T[P] // -? 移除可选
}
// 使用示例
interface Person {
name: string
age: number
}
type ReadonlyPerson = Readonly<Person>
type PartialPerson = Partial<Person>
// 自定义映射类型:将所有属性变为可选且可为 null
type Nullable<T> = {
[P in keyof T]?: T[P] | null
}
// 映射类型修改器
interface User {
name: string
age: number
email: string
}
// 添加 readonly
type ReadonlyUser = {
readonly [K in keyof User]: User[K]
}
// 移除 readonly
type MutableUser = {
-readonly [K in keyof ReadonlyUser]: ReadonlyUser[K]
}
// 键重映射(TS 4.1+)
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface Person2 {
name: string
age: number
}
type PersonGetters = Getters<Person2>
// { getName: () => string; getAge: () => number }
// 过滤键
type RemoveKindField<T> = {
[K in keyof T as Exclude<K, 'kind'>]: T[K]
}
interface Circle {
kind: 'circle'
radius: number
}
type CircleWithoutKind = RemoveKindField<Circle> // { radius: number }
// 条件映射
type Stringify<T> = {
[K in keyof T]: string
}
interface Config {
port: number
debug: boolean
}
type StringConfig = Stringify<Config> // { port: string; debug: string }15. 条件类型(Conditional Types)详解
答案:
条件类型根据条件选择两种类型之一,语法类似于三元运算符:T extends U ? X : Y。
案例:
typescript
// 基础条件类型
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// 实际应用:非空类型
type NonNullable<T> = T extends null | undefined ? never : T
type C = NonNullable<string | null> // string
// 分布式条件类型
// 当条件类型作用于联合类型时,会分发到每个成员
type ToArray<T> = T extends any ? T[] : never
type D = ToArray<string | number> // string[] | number[]
// 防止分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type E = ToArrayNonDist<string | number> // (string | number)[]
// infer 关键字 - 类型推断
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type F = ReturnType<() => string> // string
type G = ReturnType<() => Promise<number>> // Promise<number>
// 提取函数参数类型
type Parameters<T extends (...args: any[]) => any> = T extends (
...args: infer P
) => any
? P
: never
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age}`
}
type GreetParams = Parameters<typeof greet> // [string, number]
// 提取 Promise 返回值
type Awaited<T> = T extends Promise<infer R> ? R : T
type H = Awaited<Promise<string>> // string
// 复杂条件类型:递归
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
}
interface Nested {
a: {
b: {
c: string
}
}
}
type DeepReadonlyNested = DeepReadonly<Nested>16. 什么是逆变(Contravariance)和协变(Covariance)?
答案:
- 协变(Covariance):子类型可以赋值给父类型(返回值位置)
- 逆变(Contravariance):父类型可以赋值给子类型(参数位置)
- 双变(Bivariance):两者都可以(TS 默认对函数参数)
- 不变(Invariance):必须完全相同
案例:
typescript
// 类型层次
interface Animal {
name: string
}
interface Dog extends Animal {
bark(): void
}
interface Cat extends Animal {
meow(): void
}
// 协变:数组/返回值位置
let animals: Animal[] = []
let dogs: Dog[] = [{ name: 'Rex', bark() {} }]
animals = dogs // ✅ 协变:Dog[] 可以赋值给 Animal[]
// 逆变:函数参数位置
type AnimalHandler = (animal: Animal) => void
type DogHandler = (dog: Dog) => void
let handleAnimal: AnimalHandler = animal => {
console.log(animal.name)
}
let handleDog: DogHandler = dog => {
dog.bark()
}
// TS 默认是双变(bivariant),但严格模式下会检查
// handleDog = handleAnimal; // 在严格模式下可能报错
// 实际例子
type Comparator<T> = (a: T, b: T) => number
// 可以比较 Animal 的比较器,可以安全地用于比较 Dog
let animalComparator: Comparator<Animal> = (a, b) =>
a.name.localeCompare(b.name)
let dogComparator: Comparator<Dog> = animalComparator // 逆变
// readonly 数组是协变的
interface ReadonlyArray<T> {
readonly [index: number]: T
}
let readonlyAnimals: ReadonlyArray<Animal> = dogs // ✅
// 使用 strictFunctionTypes 启用严格函数类型检查
// tsconfig.json: "strictFunctionTypes": true
// 实际应用:事件处理器
interface Event {
type: string
}
interface MouseEvent extends Event {
x: number
y: number
}
type EventHandler<E extends Event> = (event: E) => void
const handleEvent: EventHandler<Event> = e => {
console.log(e.type)
}
// 可以接受更通用的事件处理器
const handleMouse: EventHandler<MouseEvent> = handleEvent17. 什么是结构化类型(Structural Typing)?
答案:
TypeScript 使用结构化类型系统,基于类型的结构(成员)来判断兼容性,而非名义类型(名称)。
案例:
typescript
// 结构化类型 vs 名义类型
// TypeScript 是结构化的,Java/C# 是名义的
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
// 即使名称不同,结构兼容就可以赋值
let p2d: Point2D = { x: 0, y: 0 }
let p3d: Point3D = { x: 0, y: 0, z: 0 }
p2d = p3d // ✅ 可以赋值,因为 Point3D 包含 Point2D 的所有成员
// p3d = p2d; // ❌ 错误:缺少 z
// 函数参数也是结构化的
interface Named {
name: string
}
class Person {
name: string
constructor(name: string) {
this.name = name
}
}
class City {
name: string
population: number
constructor(name: string, population: number) {
this.name = name
this.population = population
}
}
function greet(n: Named): void {
console.log(`Hello, ${n.name}`)
}
const person = new Person('Tom')
const city = new City('Beijing', 20000000)
greet(person) // ✅
greet(city) // ✅ 结构兼容
// 鸭子类型
interface Quacks {
quack(): void
}
class Duck {
quack() {
console.log('Quack!')
}
}
class Person2 {
quack() {
console.log("I'm quacking like a duck!")
}
}
function makeItQuack(quacker: Quacks): void {
quacker.quack()
}
makeItQuack(new Duck()) // ✅
makeItQuack(new Person2()) // ✅ 只要会 quack 就行
// 空对象类型 - 几乎所有类型都兼容
interface Empty {}
let empty: Empty = { x: 1, y: 2 } // ✅ 任何对象都兼容18. 严格空检查(Strict Null Checks)是什么?
答案:
strictNullChecks 是 TypeScript 的重要编译选项,使 null 和 undefined 成为独立的类型,需要显式处理。
案例:
typescript
// strictNullChecks: false(默认旧行为)
// null 和 undefined 可以赋值给任何类型
// strictNullChecks: true(推荐)
// null 和 undefined 只能赋值给自身和 any
// 开启 strictNullChecks 后
function greet(name: string | null): string {
// return name.toUpperCase(); // ❌ 错误:name 可能为 null
if (name === null) {
return 'Hello, stranger!'
}
return `Hello, ${name.toUpperCase()}!` // ✅ 已排除 null
}
// 可选链操作符
interface User {
profile?: {
name?: string
email?: string
}
}
function getUserEmail(user: User): string | undefined {
// return user.profile.email; // ❌ 可能为 undefined
return user.profile?.email // ✅ 可选链
}
// 非空断言(谨慎使用)
function getLength(str: string | null): number {
return str!.length // ! 告诉编译器 str 不为 null
}
// 空值合并运算符 ??
function getDisplayName(name: string | null): string {
return name ?? 'Anonymous' // 仅在 null/undefined 时返回默认值
}
// 与 || 的区别
console.log(0 || 'default') // "default"
console.log(0 ?? 'default') // 0
// 类型守卫处理 null
function process(value: string | null | undefined): void {
if (value != null) {
// 同时检查 null 和 undefined
console.log(value.toUpperCase())
}
}
// 数组查找
type User2 = { id: number; name: string }
const users: User2[] = [{ id: 1, name: 'Tom' }]
const user = users.find(u => u.id === 999)
// user.name; // ❌ 可能为 undefined
console.log(user?.name) // ✅19. 函数重载(Function Overloads)
答案:
函数重载允许定义多个函数签名,根据参数类型和数量调用不同的实现。
案例:
typescript
// 重载签名
function add(a: number, b: number): number
function add(a: string, b: string): string
function add(a: number, b: string): string
function add(a: string, b: number): string
// 实现签名(不对外暴露)
function add(a: number | string, b: number | string): number | string {
if (typeof a === 'number' && typeof b === 'number') {
return a + b
}
return String(a) + String(b)
}
// 使用
console.log(add(1, 2)) // 3
console.log(add('Hello, ', 'World')) // "Hello, World"
console.log(add(1, ' items')) // "1 items"
// 更复杂的重载示例
interface User {
type: 'user'
name: string
age: number
}
interface Admin {
type: 'admin'
name: string
role: string
}
// 根据输入类型返回不同类型
function getPerson(type: 'user', name: string, age: number): User
function getPerson(type: 'admin', name: string, role: string): Admin
function getPerson(
type: 'user' | 'admin',
name: string,
detail: number | string
): User | Admin {
if (type === 'user') {
return { type: 'user', name, age: detail as number }
}
return { type: 'admin', name, role: detail as string }
}
const user = getPerson('user', 'Tom', 25) // 推断为 User
const admin = getPerson('admin', 'Admin', 'superuser') // 推断为 Admin
// 类方法重载
class Calculator {
// 重载签名
calculate(a: number, b: number): number
calculate(a: string, b: string): string
calculate(a: number[], b: number[]): number[]
// 实现
calculate(
a: number | string | number[],
b: number | string | number[]
): number | string | number[] {
if (Array.isArray(a) && Array.isArray(b)) {
return a.map((val, i) => val + (b[i] || 0))
}
if (typeof a === 'number' && typeof b === 'number') {
return a + b
}
return String(a) + String(b)
}
}
const calc = new Calculator()
console.log(calc.calculate(1, 2))
console.log(calc.calculate([1, 2], [3, 4]))20. 元组(Tuple)类型详解
答案:
元组是固定长度、固定类型的数组,每个位置有特定类型。
案例:
typescript
// 基础元组
let person: [string, number] = ['Tom', 25]
// person = [25, "Tom"]; // ❌ 类型顺序错误
// 可选元素
let optionalTuple: [string, number?] = ['Tom']
optionalTuple = ['Tom', 25] // ✅
// 剩余元素
let restTuple: [string, ...number[]] = ['scores', 90, 85, 95]
// 只读元组
let readonlyTuple: readonly [string, number] = ['Tom', 25]
// readonlyTuple[0] = "Jerry"; // ❌ 只读
// 具名元组(TS 4.0+)
let namedTuple: [name: string, age: number] = ['Tom', 25]
// 解构元组
const [name, age] = person
console.log(name, age)
// 元组作为函数返回类型
function getCoordinates(): [number, number, number] {
return [10, 20, 30]
}
const [x, y, z] = getCoordinates()
// 元组与数组的区别
let arr: (string | number)[] = [1, 'a', 2, 'b'] // 顺序不固定
let tup: [string, number] = ['a', 1] // 顺序固定
// 使用场景:API 响应
interface ApiResponse<T> {
data: T
status: number
}
type ApiResult<T> = [data: T | null, error: Error | null]
async function fetchData<T>(url: string): Promise<ApiResult<T>> {
try {
const response = await fetch(url)
const data = await response.json()
return [data, null]
} catch (error) {
return [null, error as Error]
}
}
// 使用
const [data, error] = await fetchData<User>('/api/user')
if (error) {
console.error(error)
} else {
console.log(data)
}
// 元组类型推断
function tail<T extends any[]>(arr: readonly [any, ...T]) {
const [_ignored, ...rest] = arr
return rest
}
const myTuple = [1, 2, 3, 4] as const
const r = tail(myTuple) // 推断为 [2, 3, 4]21. 字面量类型(Literal Types)
答案:
字面量类型允许将具体的值作为类型,包括字符串、数字、布尔字面量。
案例:
typescript
// 字符串字面量类型
type Direction = 'north' | 'south' | 'east' | 'west'
function move(direction: Direction): void {
console.log(`Moving ${direction}`)
}
move('north') // ✅
// move("up"); // ❌ 错误
// 数字字面量类型
type OneToFive = 1 | 2 | 3 | 4 | 5
type HttpStatus = 200 | 404 | 500
function setStatus(code: HttpStatus): void {
console.log(`Status: ${code}`)
}
// 布尔字面量类型
interface ValidationResult {
isValid: true
message: string
}
interface ValidationError {
isValid: false
errors: string[]
}
type Validation = ValidationResult | ValidationError
function handleValidation(result: Validation): void {
if (result.isValid) {
console.log(result.message)
} else {
console.log(result.errors)
}
}
// 模板字面量类型(TS 4.1+)
type EventName<T extends string> = `on${Capitalize<T>}`
type ClickEvent = EventName<'click'> // "onClick"
// 结合使用
type CSSUnit = 'px' | 'em' | 'rem' | '%'
type CSSValue = `${number}${CSSUnit}`
const width1: CSSValue = '100px' // ✅
const width2: CSSValue = '50%' // ✅
// const width3: CSSValue = "abc"; // ❌
// 对象属性的字面量类型
interface Config {
mode: 'development' | 'production' | 'test'
logLevel: 0 | 1 | 2 | 3
strict: true
}
// as const 断言创建字面量类型
const config = {
host: 'localhost',
port: 3000,
debug: true
} as const
// config.port = 4000; // ❌ 只读且类型为字面量 3000
// 实用工具:创建事件名称
type MouseEvent = 'click' | 'dblclick' | 'mousedown' | 'mouseup'
type KeyboardEvent = 'keydown' | 'keyup' | 'keypress'
type EventType = MouseEvent | KeyboardEvent
type EventHandlerMap = {
[K in EventType as `on${Capitalize<K>}`]: (e: Event) => void
}
/*
{
onClick: (e: Event) => void;
onDblclick: (e: Event) => void;
...
}
*/22. 什么是 never 类型?
答案:
never 表示永不存在的值的类型,用于:
- 函数永不返回(抛出异常或无限循环)
- 穷尽性检查
- 从联合类型中排除
案例:
typescript
// 1. 永不返回的函数
function throwError(message: string): never {
throw new Error(message)
}
function infiniteLoop(): never {
while (true) {
console.log('Running...')
}
}
// 2. 穷尽性检查
interface Circle {
kind: 'circle'
radius: number
}
interface Square {
kind: 'square'
side: number
}
interface Triangle {
kind: 'triangle'
base: number
height: number
}
type Shape = Circle | Square | Triangle
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'square':
return shape.side ** 2
case 'triangle':
return (shape.base * shape.height) / 2
default:
// 穷尽性检查:如果 Shape 新增类型,这里会报错
const _exhaustiveCheck: never = shape
return _exhaustiveCheck
}
}
// 3. 从联合类型中排除
type NonNullable<T> = T extends null | undefined ? never : T
type A = NonNullable<string | null | undefined> // string
// 4. 过滤对象属性
type RemoveString<T> = {
[K in keyof T]: T[K] extends string ? never : K
}[keyof T]
interface Mixed {
a: string
b: number
c: boolean
d: string
}
type NonStringKeys = RemoveString<Mixed> // "b" | "c"
// 5. 简化条件类型
type Diff<T, U> = T extends U ? never : T
type Filter<T, U> = T extends U ? T : never
type B = Diff<'a' | 'b' | 'c', 'a' | 'c'> // "b"
type C = Filter<'a' | 'b' | 'c', 'a' | 'c'> // "a" | "c"
// 6. 验证不可能的状态
function assertUnreachable(x: never): never {
throw new Error("Didn't expect to get here")
}
// 7. 与 any 的区别
let anyValue: any = 'anything'
let neverValue: never
anyValue = neverValue // ✅ never 可以赋值给任何类型
// neverValue = anyValue; // ❌ 没有任何类型可以赋值给 never23. void 和 undefined 的区别?
答案:
void:表示函数没有返回值,或返回值被忽略undefined:表示值未定义,是一个具体的值
案例:
typescript
// void 类型
function logMessage(message: string): void {
console.log(message)
// 没有 return 语句,或 return; 不带值
}
// undefined 类型
function getNothing(): undefined {
return undefined
}
// 关键区别
let voidValue: void = undefined // ✅ void 可以赋值为 undefined
let undefinedValue: undefined = undefined
// voidValue = undefinedValue; // ✅
// undefinedValue = voidValue; // ❌ 通常不允许(strictNullChecks)
// 函数返回 void 时,实际可以返回任何值,但会被忽略
function doSomething(): void {
return 42 // ✅ 不报错,但返回值被忽略
}
const result = doSomething() // result 类型为 void
// 回调函数中的区别
interface Button {
onClick: () => void // 不关心返回值
}
const button: Button = {
onClick: () => {
return 'clicked' // ✅ 允许,但返回值被忽略
}
}
// 如果需要确保返回 undefined
interface StrictButton {
onClick: () => undefined
}
// const strictButton: StrictButton = {
// onClick: () => "clicked" // ❌ 错误
// };
// 实际应用
const numbers = [1, 2, 3]
// forEach 回调返回 void
numbers.forEach(num => {
console.log(num)
return num * 2 // 返回值被忽略
})
// map 回调返回具体类型
const doubled = numbers.map(num => num * 2) // number[]
// 类型兼容性
function fn1(): void {}
function fn2(): undefined {
return undefined
}
let v1: () => void = fn1
let v2: () => void = fn2 // ✅ undefined 可以赋值给 void24. 什么是 this 类型?
答案:
TypeScript 支持显式声明 this 类型,用于限制函数中 this 的上下文。
案例:
typescript
// 显式 this 参数
interface User {
name: string
greet(this: User): void
}
const user: User = {
name: 'Tom',
greet(this: User) {
console.log(`Hello, I'm ${this.name}`)
}
}
user.greet() // ✅
const greet = user.greet
// greet(); // ❌ 错误:this 类型不匹配
// 链式调用中的 this 类型
class Calculator {
private value: number = 0
add(n: number): this {
this.value += n
return this
}
multiply(n: number): this {
this.value *= n
return this
}
getValue(): number {
return this.value
}
}
const calc = new Calculator()
const result = calc.add(5).multiply(2).getValue() // 10
// 继承中的 this 类型
class AdvancedCalculator extends Calculator {
power(n: number): this {
this.value **= n
return this
}
}
const advanced = new AdvancedCalculator()
advanced.add(2).power(3).multiply(2) // 链式调用返回正确的 this 类型
// this 类型在回调中的使用
interface EventEmitter {
on(event: string, callback: (this: this, data: any) => void): void
}
// 类型安全的 this 推断
function fancyDate(this: Date): string {
return `${this.getDate()}/${this.getMonth() + 1}/${this.getFullYear()}`
}
fancyDate.call(new Date()) // ✅
// fancyDate(); // ❌ 错误
// 类中的 this 类型
class Box<T> {
content: T
constructor(content: T) {
this.content = content
}
sameAs(other: this): boolean {
return other.content === this.content
}
}
class DerivedBox extends Box<string> {
otherContent: string = '???'
}
const base = new Box('hello')
const derived = new DerivedBox('hello')
// base.sameAs(derived); // ❌ 错误:类型不兼容25. 什么是抽象类(Abstract Class)?
答案:
抽象类是不能被实例化的基类,可以包含抽象方法(只有声明没有实现),必须由派生类实现。
案例:
typescript
// 抽象类
abstract class Animal {
// 普通属性
name: string
constructor(name: string) {
this.name = name
}
// 普通方法
move(): void {
console.log(`${this.name} is moving`)
}
// 抽象方法 - 必须在子类中实现
abstract makeSound(): void
// 抽象属性(通过 getter/setter)
abstract get species(): string
}
// 不能实例化抽象类
// const animal = new Animal("Generic"); // ❌ 错误
// 具体实现
class Dog extends Animal {
constructor(name: string) {
super(name)
}
makeSound(): void {
console.log('Woof!')
}
get species(): string {
return 'Canis lupus'
}
fetch(): void {
console.log(`${this.name} is fetching`)
}
}
class Cat extends Animal {
makeSound(): void {
console.log('Meow!')
}
get species(): string {
return 'Felis catus'
}
}
// 使用
const dog = new Dog('Rex')
dog.move()
dog.makeSound()
dog.fetch()
const cat = new Cat('Whiskers')
cat.move()
cat.makeSound()
// 抽象类作为类型
function interactWithAnimal(animal: Animal): void {
animal.makeSound()
// animal.fetch(); // ❌ 错误:Animal 类型没有 fetch 方法
}
// 抽象类与接口的区别
interface Flyable {
fly(): void
}
abstract class Bird extends Animal implements Flyable {
abstract fly(): void
}
class Eagle extends Bird {
makeSound(): void {
console.log('Screech!')
}
get species(): string {
return 'Aquila chrysaetos'
}
fly(): void {
console.log(`${this.name} is soaring high`)
}
}
// 模板方法模式
abstract class DataParser<T> {
// 模板方法
parse(data: string): T {
const cleaned = this.clean(data)
const validated = this.validate(cleaned)
return this.transform(validated)
}
// 具体方法
protected clean(data: string): string {
return data.trim()
}
// 抽象方法
protected abstract validate(data: string): string
protected abstract transform(data: string): T
}
class JsonParser extends DataParser<object> {
protected validate(data: string): string {
if (!data.startsWith('{')) {
throw new Error('Invalid JSON')
}
return data
}
protected transform(data: string): object {
return JSON.parse(data)
}
}
const parser = new JsonParser()
const parsed = parser.parse('{"name": "Tom"}')第26-40题:高级类型与泛型
26. 泛型约束(Generic Constraints)详解
答案:
泛型约束限制泛型参数必须满足特定条件,使用 extends 关键字。
案例:
typescript
// 基础约束
interface HasLength {
length: number
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length)
return arg
}
logLength('hello') // ✅
logLength([1, 2, 3]) // ✅
logLength({ length: 10 }) // ✅
// logLength(42); // ❌ number 没有 length
// 多个约束
interface Printable {
print(): void
}
interface Serializable {
serialize(): string
}
function processItem<T extends Printable & Serializable>(item: T): void {
item.print()
const serialized = item.serialize()
console.log(serialized)
}
// 键约束
type KeyOf<T> = keyof T
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const person = { name: 'Tom', age: 25 }
const name = getProperty(person, 'name') // string
const age = getProperty(person, 'age') // number
// getProperty(person, "gender"); // ❌ 错误
// 构造函数约束
interface Constructor<T> {
new (...args: any[]): T
}
function createInstance<T>(Constructor: Constructor<T>): T {
return new Constructor()
}
class User {
name = 'Anonymous'
}
const user = createInstance(User)
// 递归约束
type TreeNode<T> = {
value: T
children?: TreeNode<T>[]
}
function traverseTree<T>(
node: TreeNode<T>,
callback: (value: T) => void
): void {
callback(node.value)
node.children?.forEach(child => traverseTree(child, callback))
}
// 使用示例
const tree: TreeNode<number> = {
value: 1,
children: [{ value: 2 }, { value: 3, children: [{ value: 4 }] }]
}
traverseTree(tree, console.log) // 1, 2, 3, 4
// 条件约束
type NonEmptyArray<T> = [T, ...T[]]
function firstElement<T extends readonly unknown[]>(
arr: T
): T extends NonEmptyArray<infer E> ? E : undefined {
return arr[0] as any
}
const first1 = firstElement([1, 2, 3]) // number
const first2 = firstElement([]) // undefined27. 泛型默认值
答案:
泛型可以指定默认值,当无法推断类型时使用默认值。
案例:
typescript
// 基础默认值
type MyArray<T = string> = T[]
type StringArray = MyArray // string[]
type NumberArray = MyArray<number> // number[]
// 函数中的默认值
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value)
}
const strings = createArray(3, 'hello') // string[]
const numbers = createArray<number>(3, 0) // number[]
// 多泛型参数默认值
interface Container<T, U = T> {
data: T
metadata: U
}
const container1: Container<number> = {
data: 42,
metadata: 100 // number
}
const container2: Container<number, string> = {
data: 42,
metadata: 'info' // string
}
// 基于其他泛型的默认值
interface FetchOptions<T, R = T> {
url: string
transform?: (data: T) => R
}
async function fetchData<T, R = T>(options: FetchOptions<T, R>): Promise<R> {
const response = await fetch(options.url)
const data: T = await response.json()
return options.transform ? options.transform(data) : (data as unknown as R)
}
// 使用
interface User {
id: number
name: string
}
interface UserView {
displayName: string
}
// 不转换
const users = await fetchData<User[]>({ url: '/api/users' })
// 转换
const userViews = await fetchData<User[], UserView[]>({
url: '/api/users',
transform: users => users.map(u => ({ displayName: u.name }))
})
// 类中的默认值
class Store<T = any> {
private data: T[] = []
add(item: T): void {
this.data.push(item)
}
getAll(): T[] {
return this.data
}
}
const stringStore = new Store() // Store<any>,不推荐
const numberStore = new Store<number>() // Store<number>
// 约束 + 默认值
type EventMap = Record<string, any>
interface EventEmitter<T extends EventMap = {}> {
on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void
emit<K extends keyof T>(event: K, data: T[K]): void
}
// 使用
interface MyEvents {
userLogin: { userId: string }
userLogout: { userId: string; timestamp: number }
}
const emitter: EventEmitter<MyEvents> = {
on(event, callback) {
// 实现
},
emit(event, data) {
// 实现
}
}
emitter.on('userLogin', data => {
console.log(data.userId) // 类型安全
})28. 条件类型中的 infer 关键字
答案:
infer 用于在条件类型中推断类型变量,常用于提取类型信息。
案例:
typescript
// 提取返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function greet(name: string): string {
return `Hello, ${name}`
}
type GreetReturn = ReturnType<typeof greet> // string
// 提取参数类型
type Parameters<T extends (...args: any[]) => any> = T extends (
...args: infer P
) => any
? P
: never
type GreetParams = Parameters<typeof greet> // [name: string]
// 提取 Promise 返回值
type Awaited<T> = T extends Promise<infer R> ? R : T
async function fetchUser(): Promise<{ id: number; name: string }> {
return { id: 1, name: 'Tom' }
}
type UserType = Awaited<ReturnType<typeof fetchUser>> // { id: number; name: string }
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never
type Num = ElementType<number[]> // number
type Str = ElementType<string[]> // string
// 提取函数类型的 this 参数
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
? U
: unknown
function greetPerson(this: { name: string }): void {
console.log(this.name)
}
type ThisType = ThisParameterType<typeof greetPerson> // { name: string }
// 提取构造函数实例类型
type InstanceType<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: any[]) => infer R ? R : any
class Person {
name = 'Tom'
}
type PersonInstance = InstanceType<typeof Person> // Person
// 提取元组类型的元素
type Head<T extends readonly unknown[]> = T extends [infer H, ...unknown[]]
? H
: never
type Tail<T extends readonly unknown[]> = T extends [unknown, ...infer R]
? R
: never
type H = Head<[1, 2, 3]> // 1
type T = Tail<[1, 2, 3]> // [2, 3]
// 递归提取嵌套类型
type DeepAwaited<T> = T extends Promise<infer R> ? DeepAwaited<R> : T
type NestedPromise = Promise<Promise<string>>
type Unwrapped = DeepAwaited<NestedPromise> // string
// 提取对象属性类型
type PropType<T, K extends keyof T> = T extends { [P in K]: infer V }
? V
: never
interface User {
name: string
age: number
}
type NameType = PropType<User, 'name'> // string29. 递归类型(Recursive Types)
答案:
递归类型是引用自身的类型,用于表示嵌套结构如树、链表等。
案例:
typescript
// 递归类型别名
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue }
const json: JSONValue = {
name: 'Tom',
age: 25,
hobbies: ['reading', 'coding'],
address: {
city: 'Beijing',
coordinates: [116.4, 39.9]
}
}
// 递归树类型
type TreeNode<T> = {
value: T
children?: TreeNode<T>[]
}
const tree: TreeNode<string> = {
value: 'root',
children: [
{
value: 'child1',
children: [{ value: 'grandchild1' }, { value: 'grandchild2' }]
},
{ value: 'child2' }
]
}
// 递归链表类型
type LinkedList<T> = {
value: T
next: LinkedList<T> | null
}
const list: LinkedList<number> = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: null
}
}
}
// 递归条件类型
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
interface Nested {
a: {
b: {
c: string
}
}
}
type ReadonlyNested = DeepReadonly<Nested>
// { readonly a: { readonly b: { readonly c: string } } }
// 递归类型工具:DeepPartial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
// 递归类型工具:DeepRequired
type DeepRequired<T> = {
[K in keyof T]-?: T[K] extends object ? DeepRequired<T[K]> : T[K]
}
// 递归类型限制深度
type DeepReadonlyLimited<T, Depth extends number = 5> = [Depth] extends [never]
? T
: T extends object
? { readonly [K in keyof T]: DeepReadonlyLimited<T[K], Prev<Depth>> }
: T
type Prev<T extends number> = [-1, 0, 1, 2, 3, 4, 5][T]
// 实际应用:深度合并
type DeepMerge<T, U> = {
[K in keyof T | keyof U]: K extends keyof U
? K extends keyof T
? T[K] extends object
? U[K] extends object
? DeepMerge<T[K], U[K]>
: U[K]
: U[K]
: U[K]
: K extends keyof T
? T[K]
: never
}
// 使用
interface A {
a: { x: number }
b: string
}
interface B {
a: { y: string }
c: boolean
}
type Merged = DeepMerge<A, B>
// { a: { x: number; y: string }; b: string; c: boolean }30. 什么是 keyof 操作符?
答案:
keyof 操作符提取对象类型的所有键,返回一个联合类型。
案例:
typescript
// 基础用法
interface Person {
name: string
age: number
email: string
}
type PersonKeys = keyof Person // "name" | "age" | "email"
// 与索引访问结合
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key]
}
const person: Person = { name: 'Tom', age: 25, email: 'tom@example.com' }
const name = getProperty(person, 'name') // string
// 用于类型映射
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
type Optional<T> = {
[K in keyof T]?: T[K]
}
// keyof 与 typeof 结合
const config = {
host: 'localhost',
port: 3000,
debug: true
}
type ConfigKeys = keyof typeof config // "host" | "port" | "debug"
// 处理索引签名
interface Dictionary {
[key: string]: number
}
type DictKeys = keyof Dictionary // string | number
// 实际应用:事件处理器
type EventMap = {
click: { x: number; y: number }
submit: { data: FormData }
cancel: void
}
type EventNames = keyof EventMap // "click" | "submit" | "cancel"
function handleEvent<E extends keyof EventMap>(
event: E,
data: EventMap[E]
): void {
console.log(`Handling ${event}`, data)
}
handleEvent('click', { x: 10, y: 20 })
// handleEvent("click", { x: 10 }); // ❌ 缺少 y31. 什么是 typeof 操作符?
答案:
TypeScript 中的 typeof 操作符可以获取变量或表达式的类型,用于类型上下文。
案例:
typescript
// 获取变量类型
const person = {
name: 'Tom',
age: 25
}
type Person = typeof person // { name: string; age: number }
// 获取函数类型
function greet(name: string): string {
return `Hello, ${name}`
}
type GreetFunction = typeof greet // (name: string) => string
// 获取类类型
class User {
name = ''
age = 0
}
type UserClass = typeof User // new () => User
// 结合 ReturnType
type UserInstance = InstanceType<typeof User> // User
// 用于类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string'
}
// 实用案例:从 API 响应推断类型
const apiResponse = {
data: {
users: [{ id: 1, name: 'Tom' }],
total: 100
},
status: 200,
message: 'OK'
}
type ApiResponseType = typeof apiResponse
// 可以直接使用这个类型
// 结合 keyof 使用
const actions = {
increment: (n: number) => n + 1,
decrement: (n: number) => n - 1,
reset: () => 0
}
type ActionTypes = keyof typeof actions // "increment" | "decrement" | "reset"
type ActionFunctions = (typeof actions)[ActionTypes]
// 获取枚举类型
const enum Status {
Pending = 'PENDING',
Success = 'SUCCESS',
Error = 'ERROR'
}
type StatusType = typeof Status // { Pending: "PENDING"; Success: "SUCCESS"; Error: "ERROR" }32. 什么是 in 操作符?
答案:
in 操作符在类型系统中用于遍历联合类型的成员,常用于映射类型。
案例:
typescript
// 基础映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 重映射键
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface Person {
name: string
age: number
}
type PersonGetters = Getters<Person>
// { getName: () => string; getAge: () => number }
// 过滤键
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K]
}
interface Mixed {
a: string
b: number
c: string
d: boolean
}
type StringProps = PickByType<Mixed, string> // { a: string; c: string }
// 创建事件处理器映射
type EventMap = {
click: { x: number; y: number }
submit: { data: FormData }
}
type EventHandlers = {
[K in keyof EventMap as `on${Capitalize<K & string>}`]: (
event: EventMap[K]
) => void
}
// 结果:
// {
// onClick: (event: { x: number; y: number }) => void;
// onSubmit: (event: { data: FormData }) => void;
// }
// 联合类型遍历
type EventNames = 'click' | 'submit' | 'cancel'
type EventRecord = {
[K in EventNames]: number
}
// { click: number; submit: number; cancel: number }
// 结合条件类型
type Nullable<T> = {
[K in keyof T]: T[K] | null
}
type DeepNullable<T> = {
[K in keyof T]: T[K] extends object ? DeepNullable<T[K]> : T[K] | null
}33. 什么是 is 操作符?
答案:
is 操作符用于创建类型谓词(Type Predicate),是自定义类型保护函数的返回类型。
案例:
typescript
// 基础类型谓词
interface Fish {
swim(): void
}
interface Bird {
fly(): void
}
type Animal = Fish | Bird
function isFish(animal: Animal): animal is Fish {
return (animal as Fish).swim !== undefined
}
function move(animal: Animal): void {
if (isFish(animal)) {
animal.swim() // TypeScript 知道这是 Fish
} else {
animal.fly() // TypeScript 知道这是 Bird
}
}
// 泛型类型谓词
function isOfType<T>(value: unknown, type: string): value is T {
return typeof value === type
}
function processValue(value: unknown): void {
if (isOfType<string>(value, 'string')) {
console.log(value.toUpperCase())
} else if (isOfType<number>(value, 'number')) {
console.log(value.toFixed(2))
}
}
// 数组过滤中的类型谓词
interface Cat {
meow(): void
}
interface Dog {
bark(): void
}
type Pet = Cat | Dog
function isCat(pet: Pet): pet is Cat {
return 'meow' in pet
}
const pets: Pet[] = [{ meow() {} }, { bark() {} }, { meow() {} }]
const cats = pets.filter(isCat) // Cat[]
// 更复杂的类型谓词
interface Admin {
role: 'admin'
permissions: string[]
}
interface User {
role: 'user'
email: string
}
type Person = Admin | User
function isAdmin(person: Person): person is Admin {
return person.role === 'admin'
}
function getPermissions(person: Person): string[] {
if (isAdmin(person)) {
return person.permissions
}
return []
}
// 与 instanceof 结合
class CatClass {
meow() {
console.log('Meow')
}
}
class DogClass {
bark() {
console.log('Woof')
}
}
function isCatInstance(animal: CatClass | DogClass): animal is CatClass {
return animal instanceof CatClass
}34. 什么是 satisfies 关键字(TS 4.9+)?
答案:
satisfies 关键字用于检查表达式是否满足某个类型,同时保留表达式的具体类型推断。
案例:
typescript
// 基础用法
const config = {
host: 'localhost',
port: 3000,
debug: true
} satisfies { host: string; port: number; debug: boolean }
// 保留具体字面量类型
// config.port 的类型是 3000,而不是 number
const portValue = config.port // 3000
// 对比:使用类型注解会丢失具体类型
const config2: { host: string; port: number } = {
host: 'localhost',
port: 3000
}
const port2 = config2.port // number,不是 3000
// 实际应用:配置对象
type Config = {
host: string
port: number
routes: Record<string, string>
}
const appConfig = {
host: 'localhost',
port: 3000,
routes: {
home: '/',
about: '/about',
contact: '/contact'
}
} satisfies Config
// 可以安全地访问具体键
const homeRoute = appConfig.routes.home // "/"(字面量类型)
// 检查额外属性(但允许)
const extendedConfig = {
host: 'localhost',
port: 3000,
debug: true, // 额外属性
ssl: false // 额外属性
} satisfies Config // ✅ 仍然满足 Config
// 与 as const 结合
const strictConfig = {
host: 'localhost',
port: 3000
} as const satisfies { host: string; port: number }
// strictConfig.host 的类型是 "localhost"(字面量)
// strictConfig.port 的类型是 3000(字面量)
// 数组使用 satisfies
const colors = ['red', 'green', 'blue'] satisfies string[]
// colors 的类型是 ("red" | "green" | "blue")[]
// 检查函数返回值
type Handler = (event: Event) => void
const clickHandler = ((e: MouseEvent) => {
console.log(e.clientX, e.clientY)
}) satisfies Handler // ✅ MouseEvent 是 Event 的子类型35. 什么是 unique symbol?
答案:
unique symbol 是一种特殊的 symbol 类型,每个 unique symbol 都是唯一的,不能赋值给其他 unique symbol。
案例:
typescript
// 声明 unique symbol
declare const brandKey: unique symbol
// 使用 unique symbol 创建品牌类型
type Brand<T, B> = T & { [brandKey]: B }
// 创建品牌类型
type UserId = Brand<number, 'UserId'>
type ProductId = Brand<number, 'ProductId'>
function createUserId(id: number): UserId {
return id as UserId
}
function createProductId(id: number): ProductId {
return id as ProductId
}
const userId = createUserId(1)
const productId = createProductId(1)
// 即使底层都是 number,也不能混用
// function getUser(id: UserId): User { ... }
// getUser(productId); // ❌ 错误:ProductId 不能赋值给 UserId
// 实际应用:防止 ID 混淆
interface User {
id: UserId
name: string
}
interface Product {
id: ProductId
name: string
}
function findUserById(id: UserId): User | undefined {
// 实现
return undefined
}
function findProductById(id: ProductId): Product | undefined {
// 实现
return undefined
}
// 编译时检查
const uid = createUserId(123)
findUserById(uid) // ✅
// findProductById(uid); // ❌ 编译错误
// 多个 unique symbol
declare const userBrand: unique symbol
declare const adminBrand: unique symbol
type User2 = { [userBrand]: true; name: string }
type Admin = { [adminBrand]: true; name: string; permissions: string[] }
// 确保类型不兼容
const user2: User2 = { [userBrand]: true, name: 'Tom' }
// const admin: Admin = user2; // ❌ 错误36. 什么是装饰器(Decorators)?
答案:
装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性或参数上,用于修改类的行为。
案例:
typescript
// 启用装饰器需要在 tsconfig.json 中设置:
// "experimentalDecorators": true
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor)
Object.seal(constructor.prototype)
}
@sealed
class Greeter {
greeting: string
constructor(message: string) {
this.greeting = message
}
greet() {
return 'Hello, ' + this.greeting
}
}
// 方法装饰器
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value
}
}
class Example {
@enumerable(false)
greet() {
return 'Hello'
}
}
// 属性装饰器
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value: string
Object.defineProperty(target, propertyKey, {
get: () => value,
set: newValue => {
value = formatString.replace('%s', newValue)
},
enumerable: true,
configurable: true
})
}
}
class User {
@format('Hello, %s')
name: string = ''
}
// 实际应用:日志装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args)
const result = originalMethod.apply(this, args)
console.log(`${propertyKey} returned:`, result)
return result
}
return descriptor
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b
}
}
const calc = new Calculator()
calc.add(2, 3) // 会打印日志
// 装饰器工厂
function delay(ms: number) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value
descriptor.value = function (...args: any[]) {
setTimeout(() => original.apply(this, args), ms)
}
}
}
class Greeter2 {
@delay(1000)
greet() {
console.log('Hello!')
}
}37. 什么是命名空间(Namespace)?
答案:
命名空间用于组织代码,避免全局命名冲突,可以包含类、接口、函数、变量等。
案例:
typescript
// 基础命名空间
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean
}
const lettersRegexp = /^[A-Za-z]+$/
const numberRegexp = /^[0-9]+$/
export class LettersOnlyValidator implements StringValidator {
isValid(s: string): boolean {
return lettersRegexp.test(s)
}
}
export class ZipCodeValidator implements StringValidator {
isValid(s: string): boolean {
return s.length === 5 && numberRegexp.test(s)
}
}
}
// 使用
const validators: { [s: string]: Validation.StringValidator } = {}
validators['ZIP code'] = new Validation.ZipCodeValidator()
validators['Letters only'] = new Validation.LettersOnlyValidator()
// 嵌套命名空间
namespace Outer {
export namespace Inner {
export const value = 42
}
}
console.log(Outer.Inner.value)
// 命名空间与模块的区别
// 命名空间:编译后合并到一个文件,用于组织内部代码
// 模块:ES Module,每个文件是一个模块
// 命名空间别名
import V = Validation
const validator = new V.LettersOnlyValidator()
// 多文件命名空间(使用 reference 标签)
// /// <reference path="Validation.ts" />
// /// <reference path="LettersOnlyValidator.ts" />
// 现代替代方案:使用 ES Module
// validation.ts
export interface Validator {
isValid(s: string): boolean
}
export class EmailValidator implements Validator {
isValid(s: string): boolean {
return s.includes('@')
}
}
// 使用
// import { EmailValidator } from "./validation";38. 什么是三斜线指令(Triple-Slash Directives)?
答案:
三斜线指令是包含单个 XML 标签的单行注释,用于指定编译器指令。
案例:
typescript
// 引用其他文件
/// <reference path="./types.d.ts" />
// 引用类型声明包
/// <reference types="node" />
// 引用 AMD 模块
/// <reference path="module.ts" />
// 指定库文件
/// <reference lib="es2020" />
/// <reference lib="dom" />
// 实际应用场景
// 1. 声明文件引用
// types.d.ts
interface Window {
myLib: any;
}
// app.ts
/// <reference path="./types.d.ts" />
window.myLib.doSomething();
// 2. 类型包引用
// 当使用 @types 包时
/// <reference types="jquery" />
$("#app").hide();
// 3. 指定编译目标
// tsconfig.json 中指定更常见:
// "lib": ["es2020", "dom"]
// 现代替代方案
// 现在更推荐使用 tsconfig.json 和 ES Module import
// 而不是三斜线指令
// tsconfig.json 示例
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"types": ["node", "jest"]
}
}39. 什么是声明合并(Declaration Merging)?
答案:
声明合并是 TypeScript 编译器将多个同名声明合并为一个声明的能力。
案例:
typescript
// 接口声明合并
interface Person {
name: string
}
interface Person {
age: number
}
// 合并后:
// interface Person {
// name: string;
// age: number;
// }
const person: Person = { name: 'Tom', age: 25 }
// 命名空间与类合并
class Album {
label: Album.AlbumLabel = new Album.AlbumLabel()
}
namespace Album {
export class AlbumLabel {
id: number = 0
}
}
const album = new Album()
const label = new Album.AlbumLabel()
// 命名空间与函数合并
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix
}
namespace buildLabel {
export let suffix = ''
export let prefix = 'Hello, '
}
console.log(buildLabel('Sam')) // "Hello, Sam"
// 命名空间与枚举合并
enum Color {
red = 1,
green = 2,
blue = 4
}
namespace Color {
export function mixColor(colorName: string) {
if (colorName == 'yellow') {
return Color.red + Color.green
}
// ...
}
}
console.log(Color.mixColor('yellow')) // 3
// 模块扩展(声明合并的实际应用)
// original-module.ts
export interface Request {
body: any
}
// extension.ts
import { Request } from './original-module'
declare module './original-module' {
interface Request {
user?: { id: string; name: string }
}
}
// 使用扩展后的类型
const req: Request = {
body: {},
user: { id: '1', name: 'Tom' }
}
// 全局扩展
declare global {
interface Window {
myApp: { version: string }
}
}
window.myApp = { version: '1.0.0' }40. 什么是模块解析策略?
答案:
TypeScript 支持两种模块解析策略:Classic 和 Node,Node 是默认策略。
案例:
typescript
// tsconfig.json 配置
{
"compilerOptions": {
"moduleResolution": "node" // 或 "classic"
}
}
// Node 模块解析策略(默认)
// 1. 相对路径导入
import { foo } from "./foo"; // 查找 ./foo.ts, ./foo.tsx, ./foo.d.ts
import { bar } from "./bar/index"; // 查找 ./bar/index.ts
// 2. 非相对路径导入
import { lodash } from "lodash"; // 查找 node_modules/lodash
// 路径映射(path mapping)
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}
// 使用路径映射
import { Button } from "@components/Button";
import { formatDate } from "@utils/date";
// 实际文件位置:
// src/components/Button.ts
// src/utils/date.ts
// 类型导入(Type-Only Imports)
import type { User } from "./types";
import { type Config, loadConfig } from "./config";
// 等同于:
// import type { User, Config } from "./types";
// import { loadConfig } from "./config";
// 命名空间导入
import * as Utils from "./utils";
Utils.formatDate(new Date());
// 默认导入
import React from "react";
// 混合导入
import React, { useState, useEffect } from "react";
// 动态导入
async function loadModule() {
const module = await import("./heavy-module");
module.doSomething();
}
// 条件类型导入(TS 4.9+)
import type { SomeType } from "some-module";第41-55题:类与接口
41. 类的访问修饰符有哪些?
答案:
TypeScript 支持三种访问修饰符:public、private、protected,控制类成员的可见性。
案例:
typescript
class Person {
// public - 默认,任何地方可访问
public name: string
// private - 仅类内部可访问
private age: number
// protected - 类内部和子类可访问
protected email: string
constructor(name: string, age: number, email: string) {
this.name = name
this.age = age
this.email = email
}
// 私有方法
private calculateBirthYear(): number {
return new Date().getFullYear() - this.age
}
// 公共方法
public getInfo(): string {
return `${this.name}, born in ${this.calculateBirthYear()}`
}
}
class Employee extends Person {
constructor(
name: string,
age: number,
email: string,
private salary: number
) {
super(name, age, email)
}
public getEmployeeInfo(): string {
// this.age; // ❌ 错误:private 成员不能在子类访问
return `${this.name} - ${this.email}` // ✅ protected 可以访问
}
}
const person = new Person('Tom', 30, 'tom@example.com')
console.log(person.name) // ✅
// console.log(person.age); // ❌ 错误
// console.log(person.email); // ❌ 错误
// 参数属性简写
class Person2 {
constructor(
public name: string,
private age: number,
protected email: string
) {}
}
// 私有字段(ES2022 标准,使用 # 前缀)
class Counter {
#count = 0 // 真正的私有字段,编译后仍然私有
increment() {
this.#count++
}
getCount() {
return this.#count
}
}
const counter = new Counter()
// counter.#count; // ❌ 编译错误,运行时也无法访问42. 什么是只读属性(readonly)?
答案:
readonly 修饰符使属性只能在声明时或构造函数中初始化,之后不能修改。
案例:
typescript
class Person {
readonly id: number
readonly birthDate: Date
name: string
constructor(id: number, name: string) {
this.id = id // ✅ 可以在构造函数中赋值
this.name = name
this.birthDate = new Date()
}
changeId(newId: number) {
// this.id = newId; // ❌ 错误:readonly 属性不能修改
}
}
const person = new Person(1, 'Tom')
// person.id = 2; // ❌ 错误
// 只读数组
const readonlyNumbers: readonly number[] = [1, 2, 3]
// readonlyNumbers.push(4); // ❌ 错误
// readonlyNumbers[0] = 0; // ❌ 错误
// Readonly 工具类型
interface Person2 {
name: string
age: number
}
type ReadonlyPerson = Readonly<Person2>
// { readonly name: string; readonly age: number }
// 只读映射类型
interface Config {
host: string
port: number
}
type FrozenConfig = {
readonly [K in keyof Config]: Config[K]
}
// 移除 readonly
type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}
// 实际应用:配置对象
const API_CONFIG = {
BASE_URL: 'https://api.example.com',
TIMEOUT: 5000,
RETRY_COUNT: 3
} as const // 所有属性变为 readonly
// API_CONFIG.TIMEOUT = 10000; // ❌ 错误43. 什么是静态成员(static)?
答案:
静态成员属于类本身,而不是类的实例,可以通过类名直接访问。
案例:
typescript
class MathUtils {
// 静态属性
static PI: number = 3.14159
// 静态只读属性
static readonly E: number = 2.71828
// 静态方法
static circleArea(radius: number): number {
return this.PI * radius * radius
}
static add(a: number, b: number): number {
return a + b
}
}
// 直接通过类访问
console.log(MathUtils.PI)
console.log(MathUtils.circleArea(5))
console.log(MathUtils.add(2, 3))
// 静态代码块(TS 4.4+)
class Database {
static connection: Connection
static {
// 静态初始化代码
this.connection = new Connection()
console.log('Database connection initialized')
}
}
// 单例模式
class Singleton {
private static instance: Singleton
private constructor() {
// 私有构造函数
}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton()
}
return Singleton.instance
}
}
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
console.log(s1 === s2) // true
// 静态成员与泛型
class Container<T> {
static count = 0 // 静态成员不能使用泛型参数
constructor(private value: T) {
Container.count++
}
}
// 抽象类的静态成员
abstract class Factory {
static create(): Product {
throw new Error('Must be implemented by subclass')
}
}44. 什么是 Getter 和 Setter?
答案:
Getter 和 Setter 是特殊的访问器,用于控制属性的读取和赋值行为。
案例:
typescript
class Person {
private _name: string = ''
private _age: number = 0
// Getter
get name(): string {
console.log('Getting name')
return this._name
}
// Setter
set name(value: string) {
console.log('Setting name')
if (value.length < 2) {
throw new Error('Name must be at least 2 characters')
}
this._name = value
}
// 带验证的 Setter
get age(): number {
return this._age
}
set age(value: number) {
if (value < 0 || value > 150) {
throw new Error('Invalid age')
}
this._age = value
}
// 只读 Getter(没有 Setter)
get info(): string {
return `${this._name}, ${this._age} years old`
}
}
const person = new Person()
person.name = 'Tom' // 调用 Setter
console.log(person.name) // 调用 Getter
// person.info = "..."; // ❌ 错误:只有 Getter
// 计算属性
class Rectangle {
constructor(
private _width: number,
private _height: number
) {}
get width() {
return this._width
}
set width(value: number) {
if (value <= 0) throw new Error('Width must be positive')
this._width = value
}
get height() {
return this._height
}
set height(value: number) {
if (value <= 0) throw new Error('Height must be positive')
this._height = value
}
get area() {
return this._width * this._height
}
get perimeter() {
return 2 * (this._width + this._height)
}
}
const rect = new Rectangle(10, 5)
console.log(rect.area) // 50
console.log(rect.perimeter) // 30
// 自动实现 Getter/Setter
class User {
constructor(
private _firstName: string,
private _lastName: string
) {}
get fullName(): string {
return `${this._firstName} ${this._lastName}`
}
}45. 类可以实现多个接口吗?
答案:
是的,TypeScript 支持类实现多个接口,使用逗号分隔。
案例:
typescript
interface Drawable {
draw(): void
}
interface Movable {
move(x: number, y: number): void
}
interface Resizable {
resize(width: number, height: number): void
}
// 实现多个接口
class Shape implements Drawable, Movable, Resizable {
constructor(
private x: number,
private y: number,
private width: number,
private height: number
) {}
draw(): void {
console.log(`Drawing shape at (${this.x}, ${this.y})`)
}
move(x: number, y: number): void {
this.x = x
this.y = y
}
resize(width: number, height: number): void {
this.width = width
this.height = height
}
}
// 接口继承多个接口
interface Interactive extends Drawable, Movable {
onClick(): void
}
class Button implements Interactive {
draw(): void {
console.log('Drawing button')
}
move(x: number, y: number): void {
console.log(`Moving button to (${x}, ${y})`)
}
onClick(): void {
console.log('Button clicked')
}
}
// 接口与抽象类的区别
// 接口:定义契约,可以有多个实现
// 抽象类:提供部分实现,只能单继承
// 实际应用:插件系统
interface Plugin {
name: string
init(): void
destroy(): void
}
interface Configurable {
configure(options: Record<string, any>): void
}
class LoggerPlugin implements Plugin, Configurable {
name = 'Logger'
private options: Record<string, any> = {}
init(): void {
console.log('Logger initialized with', this.options)
}
destroy(): void {
console.log('Logger destroyed')
}
configure(options: Record<string, any>): void {
this.options = options
}
}46. 什么是类表达式(Class Expressions)?
答案:
类表达式类似于函数表达式,可以创建匿名类或命名类,并赋值给变量。
案例:
typescript
// 匿名类表达式
const Person = class {
name: string
constructor(name: string) {
this.name = name
}
greet() {
return `Hello, ${this.name}`
}
}
const person = new Person('Tom')
console.log(person.greet())
// 命名类表达式(类名只在类内部可用)
const Calculator = class Calc {
static count = 0
constructor() {
Calc.count++
}
}
// new Calc(); // ❌ 错误:Calc 只在类内部可见
new Calculator()
// 类作为参数
function createInstance<T>(Class: new () => T): T {
return new Class()
}
class User {
name = 'Anonymous'
}
const user = createInstance(User)
// 类表达式与泛型
const Queue = class<T> {
private data: T[] = []
push(item: T) {
this.data.push(item)
}
pop(): T | undefined {
return this.data.shift()
}
}
const numberQueue = new Queue<number>()
numberQueue.push(10)
// 混入(Mixin)模式
type Constructor<T = {}> = new (...args: any[]) => T
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = new Date()
getTimestamp() {
return this.timestamp
}
}
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false
activate() {
this.isActive = true
}
deactivate() {
this.isActive = false
}
}
}
class User2 {
name = ''
}
const TimestampedUser = Timestamped(User2)
const ActivatableTimestampedUser = Activatable(TimestampedUser)
const specialUser = new ActivatableTimestampedUser()
specialUser.activate()
console.log(specialUser.getTimestamp())47. 什么是混入(Mixins)?
答案:
混入是一种通过组合而非继承来复用代码的模式,允许类组合多个来源的功能。
案例:
typescript
// 基础混入函数
type Constructor<T = {}> = new (...args: any[]) => T
// 可序列化混入
function Serializable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
serialize(): string {
return JSON.stringify(this)
}
static deserialize<T>(this: Constructor<T>, json: string): T {
return JSON.parse(json)
}
}
}
// 可记录日志混入
function Loggable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
log(message: string): void {
console.log(`[${new Date().toISOString()}] ${message}`)
}
}
}
// 基础类
class Person {
constructor(public name: string) {}
}
// 应用混入
const SerializablePerson = Serializable(Person)
const LoggableSerializablePerson = Loggable(SerializablePerson)
// 使用
const person = new LoggableSerializablePerson('Tom')
person.log('Created person')
console.log(person.serialize())
// 带参数的混入
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
createdAt = new Date()
updatedAt = new Date()
touch(): void {
this.updatedAt = new Date()
}
}
}
// 约束混入
interface Disposable {
dispose(): void
}
function DisposableMixin<TBase extends Constructor<Disposable>>(Base: TBase) {
return class extends Base {
isDisposed = false
dispose(): void {
if (this.isDisposed) {
throw new Error('Already disposed')
}
this.isDisposed = true
}
}
}
// 实际应用:模型类
class Model {
id: string = ''
}
const EnhancedModel = Timestamped(Serializable(Loggable(Model)))
class User extends EnhancedModel {
email = ''
}
const user = new User()
user.log('User created')
user.touch()
console.log(user.serialize())48. 接口如何继承类?
答案:
接口可以继承类的成员(不包括实现),但不继承 private 和 protected 成员。
案例:
typescript
class Control {
private state: any
constructor() {
this.state = {}
}
protected getState(): any {
return this.state
}
}
// 接口继承类
interface SelectableControl extends Control {
select(): void
}
// 实现接口的类必须也继承 Control
class Button extends Control implements SelectableControl {
select(): void {
// 可以访问 protected 成员
const state = this.getState()
console.log('Button selected', state)
}
}
class TextBox extends Control implements SelectableControl {
select(): void {
console.log('TextBox selected')
}
}
// 不继承 Control 的类不能实现 SelectableControl
// class Image implements SelectableControl {
// select(): void {} // ❌ 错误:缺少 state 和 getState
// }
// 实际应用:组件系统
abstract class Component {
protected element: HTMLElement
constructor(tagName: string) {
this.element = document.createElement(tagName)
}
abstract render(): void
}
interface Interactive extends Component {
onInteract(): void
}
class Button2 extends Component implements Interactive {
onInteract(): void {
console.log('Button clicked')
}
render(): void {
document.body.appendChild(this.element)
}
}
// 接口继承多个类
class Visual {
color = 'black'
}
class Interactive2 {
enabled = true
}
// 接口可以同时继承多个类
interface Button3 extends Visual, Interactive2 {
label: string
}
const button: Button3 = {
color: 'blue',
enabled: true,
label: 'Click me'
}49. 什么是可选类成员?
答案:
可选类成员使用 ? 标记,表示该成员可以不存在,访问前需要进行非空检查。
案例:
typescript
class Person {
name: string
age?: number // 可选属性
email?: string
constructor(name: string, age?: number) {
this.name = name
this.age = age
}
getInfo(): string {
let info = `Name: ${this.name}`
// 需要检查可选属性
if (this.age !== undefined) {
info += `, Age: ${this.age}`
}
if (this.email) {
info += `, Email: ${this.email}`
}
return info
}
// 可选方法
updateEmail?(email: string): void
}
const person1 = new Person('Tom', 25)
const person2 = new Person('Jerry') // age 为 undefined
// 检查可选方法
if (person1.updateEmail) {
person1.updateEmail('tom@example.com')
}
// 可选参数的方法
class Calculator {
add(a: number, b?: number): number {
if (b === undefined) {
return a
}
return a + b
}
}
// 可选的 getter/setter
class Temperature {
private _celsius?: number
private _fahrenheit?: number
get celsius(): number | undefined {
return this._celsius
}
set celsius(value: number) {
this._celsius = value
this._fahrenheit = (value * 9) / 5 + 32
}
}50. 什么是参数属性(Parameter Properties)?
答案:
参数属性是在构造函数参数前添加访问修饰符,自动创建并初始化同名属性。
案例:
typescript
// 传统写法
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
}
// 使用参数属性简写
class Person2 {
constructor(
public name: string,
public age: number
) {}
}
// 混合使用
class User {
// 普通属性
createdAt: Date = new Date()
constructor(
public id: number,
public name: string,
private password: string, // 私有参数属性
protected role: string = 'user' // 带默认值的 protected 参数属性
) {}
validatePassword(input: string): boolean {
return this.password === input
}
}
const user = new User(1, 'Tom', 'secret123')
console.log(user.name) // ✅
// console.log(user.password); // ❌ 私有属性
// readonly 参数属性
class Config {
constructor(
readonly apiKey: string,
readonly baseUrl: string,
public timeout: number = 5000
) {}
}
const config = new Config('key123', 'https://api.example.com')
// config.apiKey = "newKey"; // ❌ readonly 不能修改
config.timeout = 10000 // ✅
// 可选参数属性
class Product {
constructor(
public id: number,
public name: string,
public description?: string // 可选参数属性
) {}
}51. 如何实现接口的可选方法?
答案:
接口中可以定义可选方法,实现类可以选择性实现这些方法。
案例:
typescript
interface Plugin {
name: string
init(): void
destroy(): void
update?(): void // 可选方法
configure?(options: any): void // 可选方法
}
// 实现所有方法
class FullPlugin implements Plugin {
name = 'FullPlugin'
init(): void {
console.log('Initialized')
}
destroy(): void {
console.log('Destroyed')
}
update(): void {
console.log('Updated')
}
configure(options: any): void {
console.log('Configured with', options)
}
}
// 只实现必需方法
class SimplePlugin implements Plugin {
name = 'SimplePlugin'
init(): void {
console.log('Initialized')
}
destroy(): void {
console.log('Destroyed')
}
}
// 使用可选方法
function usePlugin(plugin: Plugin): void {
plugin.init()
// 检查可选方法是否存在
if (plugin.configure) {
plugin.configure({ debug: true })
}
// 或者使用可选链
plugin.update?.()
plugin.destroy()
}
// 索引签名与可选属性
interface DynamicPlugin extends Plugin {
[key: string]: any
}
// 严格的可选方法检查
type RequiredMethods<T> = {
[K in keyof T as T[K] extends (...args: any[]) => any ? K : never]-?: T[K]
}
type OptionalMethods<T> = {
[K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K]
}52. 什么是索引签名类?
答案:
类可以定义索引签名,允许通过索引访问动态属性。
案例:
typescript
// 字符串索引签名
class Dictionary {
[key: string]: string | undefined;
// 预定义属性必须兼容索引签名
defaultValue: string = "";
set(key: string, value: string): void {
this[key] = value;
}
get(key: string): string | undefined {
return this[key];
}
}
const dict = new Dictionary();
dict.set("name", "Tom");
console.log(dict.get("name"));
console.log(dict["name"]); // 直接索引访问
// 数字索引签名
class NumberList {
[index: number]: number;
length: number = 0;
push(value: number): void {
this[this.length] = value;
this.length++;
}
get(index: number): number {
return this[index];
}
}
const list = new NumberList();
list.push(10);
list.push(20);
// 混合索引签名
interface MixedIndex {
[key: string]: any;
[index: number]: any;
}
// 只读索引签名
class ReadonlyDict {
private data: Record<string, string> = {};
get [key: string](): string | undefined {
return this.data[key];
}
set(key: string, value: string): void {
this.data[key] = value;
}
}
// 类型安全的字典
class TypedDictionary<T> {
[key: string]: T | undefined;
set(key: string, value: T): void {
this[key] = value;
}
get(key: string): T | undefined {
return this[key];
}
}
const numberDict = new TypedDictionary<number>();
numberDict.set("age", 25);
// numberDict.set("name", "Tom"); // ❌ 错误:必须是 number53. 类如何实现索引访问类型?
答案:
类可以实现索引访问类型,通过定义索引签名来支持动态属性访问。
案例:
typescript
// 定义索引访问接口
interface StringIndexed {
[key: string]: string
}
interface NumberIndexed {
[index: number]: number
}
// 类实现索引访问
class StringMap implements StringIndexed {
[key: string]: string
constructor(initial?: Record<string, string>) {
if (initial) {
Object.assign(this, initial)
}
}
}
const map = new StringMap({ name: 'Tom', city: 'Beijing' })
console.log(map.name) // "Tom"
// 泛型索引类
class GenericMap<T> {
[key: string]: T | undefined
set(key: string, value: T): void {
this[key] = value
}
get(key: string): T | undefined {
return this[key]
}
has(key: string): boolean {
return key in this
}
delete(key: string): void {
delete this[key]
}
}
const userMap = new GenericMap<{ name: string; age: number }>()
userMap.set('user1', { name: 'Tom', age: 25 })
// 与 Proxy 结合
class Reactive<T extends object> {
private data: T
constructor(initial: T) {
this.data = new Proxy(initial, {
get: (target, key) => {
console.log(`Getting ${String(key)}`)
return target[key as keyof T]
},
set: (target, key, value) => {
console.log(`Setting ${String(key)} to ${value}`)
target[key as keyof T] = value
return true
}
})
}
get value(): T {
return this.data
}
}
const reactive = new Reactive({ count: 0 })
reactive.value.count++ // 触发 get 和 set54. 什么是类的私有字段(Private Fields)?
答案:
私有字段使用 # 前缀,是真正的私有成员,编译后仍然保持私有性,不能在类外部访问。
案例:
typescript
class Counter {
// 私有字段
#count = 0
#name: string
constructor(name: string) {
this.#name = name
}
increment(): void {
this.#count++
}
decrement(): void {
this.#count--
}
getCount(): number {
return this.#count
}
getName(): string {
return this.#name
}
}
const counter = new Counter('MyCounter')
counter.increment()
console.log(counter.getCount()) // 1
// counter.#count; // ❌ 编译错误:私有字段
// counter["#count"]; // ❌ 运行时也无法访问
// 私有字段与继承
class AdvancedCounter extends Counter {
// 子类不能访问父类的私有字段
reset(): void {
// this.#count = 0; // ❌ 错误
// 必须通过公共方法
while (this.getCount() > 0) {
// this.decrement(); // 假设有这个方法
}
}
}
// 私有静态字段
class Singleton {
static #instance: Singleton
static getInstance(): Singleton {
if (!Singleton.#instance) {
Singleton.#instance = new Singleton()
}
return Singleton.#instance
}
private constructor() {}
}
// 私有方法
class BankAccount {
#balance = 0
deposit(amount: number): void {
if (amount > 0) {
this.#balance += amount
this.#logTransaction('deposit', amount)
}
}
#logTransaction(type: string, amount: number): void {
console.log(`${type}: ${amount}`)
}
}
// 检查私有字段
class Container {
#privateValue = 42
publicValue = 100
}
const container = new Container()
// console.log(#privateValue in container); // 语法错误
// 需要使用特定语法检查55. 什么是类的 protected 构造函数?
答案:
protected 构造函数只能在类内部或子类中调用,常用于抽象基类或单例模式。
案例:
typescript
// 抽象基类模式
abstract class BaseService {
protected constructor(protected apiUrl: string) {}
abstract init(): void
protected fetch(endpoint: string): Promise<any> {
return fetch(`${this.apiUrl}/${endpoint}`)
}
}
class UserService extends BaseService {
constructor() {
super('https://api.example.com/users')
}
init(): void {
console.log('UserService initialized')
}
async getUsers(): Promise<any> {
return this.fetch('list')
}
}
// new BaseService("url"); // ❌ 错误:protected 构造函数
const userService = new UserService() // ✅
// 单例模式
class Database {
private static instance: Database
protected constructor(private connectionString: string) {}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database('default-connection')
}
return Database.instance
}
query(sql: string): any {
console.log(`Executing: ${sql}`)
}
}
// new Database("url"); // ❌ 错误
const db = Database.getInstance() // ✅
// 工厂模式
class Product {
protected constructor(
public name: string,
public price: number
) {}
static create(name: string, price: number): Product {
return new Product(name, price)
}
static createDiscounted(
name: string,
price: number,
discount: number
): Product {
return new Product(name, price * (1 - discount))
}
}
const product1 = Product.create('Laptop', 1000)
const product2 = Product.createDiscounted('Laptop', 1000, 0.1)
// new Product("Laptop", 1000); // ❌ 错误第56-70题:类型操作与工具类型
56. TypeScript 内置的工具类型有哪些?
答案:
TypeScript 提供了丰富的内置工具类型,用于常见的类型转换操作。
案例:
typescript
// 1. Partial<T> - 所有属性变为可选
interface User {
name: string
age: number
}
type PartialUser = Partial<User> // { name?: string; age?: number }
// 2. Required<T> - 所有属性变为必需
type RequiredUser = Required<PartialUser> // { name: string; age: number }
// 3. Readonly<T> - 所有属性变为只读
type ReadonlyUser = Readonly<User> // { readonly name: string; readonly age: number }
// 4. Record<K, T> - 创建键值对类型
type PageNames = 'home' | 'about' | 'contact'
type Pages = Record<PageNames, { title: string }>
// { home: { title: string }; about: { title: string }; contact: { title: string } }
// 5. Pick<T, K> - 从 T 中选取 K 属性
type UserName = Pick<User, 'name'> // { name: string }
// 6. Omit<T, K> - 从 T 中排除 K 属性
type UserWithoutAge = Omit<User, 'age'> // { name: string }
// 7. Exclude<T, U> - 从 T 中排除可赋值给 U 的类型
type T0 = Exclude<'a' | 'b' | 'c', 'a'> // "b" | "c"
// 8. Extract<T, U> - 从 T 中提取可赋值给 U 的类型
type T1 = Extract<'a' | 'b' | 'c', 'a' | 'f'> // "a"
// 9. NonNullable<T> - 排除 null 和 undefined
type T2 = NonNullable<string | number | undefined> // string | number
// 10. Parameters<T> - 提取函数参数类型
type Fn = (a: string, b: number) => void
type FnParams = Parameters<Fn> // [a: string, b: number]
// 11. ReturnType<T> - 提取函数返回类型
type FnReturn = ReturnType<Fn> // void
// 12. InstanceType<T> - 提取类实例类型
class MyClass {}
type MyInstance = InstanceType<typeof MyClass> // MyClass
// 13. ThisParameterType<T> - 提取 this 参数类型
function greet(this: { name: string }) {}
type ThisType = ThisParameterType<typeof greet> // { name: string }
// 14. OmitThisParameter<T> - 移除 this 参数类型
type GreetWithoutThis = OmitThisParameter<typeof greet> // () => void
// 15. ThisType<T> - 指定 this 类型(用于对象字面量)
interface State {
count: number
}
const methods: ThisType<State> = {
increment() {
this.count++ // this 被推断为 State
}
}
// 16. Uppercase<T>, Lowercase<T>, Capitalize<T>, Uncapitalize<T>
type T3 = Uppercase<'hello'> // "HELLO"
type T4 = Lowercase<'WORLD'> // "world"
type T5 = Capitalize<'hello'> // "Hello"
type T6 = Uncapitalize<'Hello'> // "hello"57. 如何实现 DeepPartial 类型?
答案:
DeepPartial 是递归版本的 Partial,将对象的所有嵌套属性都变为可选。
案例:
typescript
// 基础 DeepPartial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}
// 改进版:处理数组和函数
type DeepPartial2<T> = T extends Function
? T
: T extends Array<infer U>
? Array<DeepPartial2<U>>
: T extends object
? { [K in keyof T]?: DeepPartial2<T[K]> }
: T
// 使用示例
interface Company {
name: string
address: {
street: string
city: string
country: {
name: string
code: string
}
}
employees: Array<{
name: string
department: {
name: string
manager: {
name: string
}
}
}>
}
type PartialCompany = DeepPartial2<Company>
// 可以部分更新任意层级
const update: PartialCompany = {
address: {
city: 'Beijing'
}
}
// 实际应用:配置合并
function mergeConfig<T>(defaultConfig: T, userConfig: DeepPartial<T>): T {
// 递归合并实现
return { ...defaultConfig, ...userConfig } as T
}
const defaultConfig = {
server: {
port: 3000,
host: 'localhost'
},
database: {
url: 'postgres://localhost',
pool: {
min: 2,
max: 10
}
}
}
const userConfig: DeepPartial<typeof defaultConfig> = {
server: {
port: 8080
}
}
const finalConfig = mergeConfig(defaultConfig, userConfig)58. 如何实现 DeepReadonly 类型?
答案:
DeepReadonly 递归地将对象及其所有嵌套属性变为只读。
案例:
typescript
// 基础 DeepReadonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
// 改进版:正确处理数组、Map、Set、Promise
type DeepReadonly2<T> = T extends (infer R)[]
? ReadonlyArray<DeepReadonly2<R>>
: T extends Map<infer K, infer V>
? ReadonlyMap<DeepReadonly2<K>, DeepReadonly2<V>>
: T extends Set<infer M>
? ReadonlySet<DeepReadonly2<M>>
: T extends Promise<infer P>
? Promise<DeepReadonly2<P>>
: T extends object
? { readonly [K in keyof T]: DeepReadonly2<T[K]> }
: T
// 使用示例
interface State {
user: {
name: string
settings: {
theme: 'light' | 'dark'
notifications: boolean
}
}
items: Array<{
id: number
name: string
}>
}
type ImmutableState = DeepReadonly2<State>
const state: ImmutableState = {
user: {
name: 'Tom',
settings: {
theme: 'light',
notifications: true
}
},
items: [{ id: 1, name: 'Item 1' }]
}
// state.user.name = "Jerry"; // ❌ 错误:只读属性
// state.items.push({ id: 2, name: "Item 2" }); // ❌ 错误:只读数组
// 实际应用:Redux State
type RootState = DeepReadonly2<{
auth: {
user: { id: string; email: string } | null
isLoading: boolean
}
posts: Array<{
id: string
title: string
content: string
author: { id: string; name: string }
}>
}>59. 如何实现 Mutable 类型?
答案:
Mutable 类型移除 readonly 修饰符,将只读类型变为可变类型。
案例:
typescript
// 基础 Mutable
type Mutable<T> = {
-readonly [K in keyof T]: T[K]
}
// DeepMutable:递归移除 readonly
type DeepMutable<T> = T extends readonly (infer R)[]
? Array<DeepMutable<R>>
: T extends ReadonlyMap<infer K, infer V>
? Map<DeepMutable<K>, DeepMutable<V>>
: T extends ReadonlySet<infer M>
? Set<DeepMutable<M>>
: T extends object
? { -readonly [K in keyof T]: DeepMutable<T[K]> }
: T
// 使用示例
interface ReadonlyConfig {
readonly api: {
readonly url: string
readonly timeout: number
}
readonly features: readonly string[]
}
type MutableConfig = DeepMutable<ReadonlyConfig>
// {
// api: { url: string; timeout: number };
// features: string[];
// }
// 实际应用:测试中的状态修改
function createTestState<T>(state: T): DeepMutable<T> {
return JSON.parse(JSON.stringify(state))
}
const readonlyState = {
count: 0,
user: {
name: 'Tom'
}
} as const
const mutableState = createTestState(readonlyState)
mutableState.count = 10 // ✅
mutableState.user.name = 'Jerry' // ✅
// 与 Readonly 互逆
type T1 = Mutable<Readonly<{ a: string }>> // { a: string }
type T2 = Readonly<Mutable<{ readonly a: string }>> // { readonly a: string }60. 如何实现 PickByType 类型?
答案:
PickByType 根据值的类型从对象中选取属性。
案例:
typescript
// 选取指定类型的属性
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K]
}
// 排除指定类型的属性
type OmitByType<T, U> = {
[K in keyof T as T[K] extends U ? never : K]: T[K]
}
// 使用示例
interface User {
id: number
name: string
email: string
age: number
isActive: boolean
createdAt: Date
}
type StringProps = PickByType<User, string>
// { name: string; email: string }
type NumberProps = PickByType<User, number>
// { id: number; age: number }
type NonStringProps = OmitByType<User, string>
// { id: number; age: number; isActive: boolean; createdAt: Date }
// 选取函数类型的属性
type FunctionProps<T> = PickByType<T, (...args: any[]) => any>
interface Component {
render: () => void
update: (props: any) => void
state: object
props: object
}
type Methods = FunctionProps<Component> // { render: () => void; update: (props: any) => void }
// 选取可选属性
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never
}[keyof T]
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K
}[keyof T]
interface Mixed {
required: string
optional?: number
}
type OptKeys = OptionalKeys<Mixed> // "optional"
type ReqKeys = RequiredKeys<Mixed> // "required"61. 如何实现 Flatten 类型?
答案:
Flatten 类型将嵌套数组类型展平为一维数组。
案例:
typescript
// 基础 Flatten
type Flatten<T extends any[]> = T extends [infer First, ...infer Rest]
? First extends any[]
? [...Flatten<First>, ...Flatten<Rest>]
: [First, ...Flatten<Rest>]
: []
// 使用示例
type T1 = Flatten<[1, [2, 3], [[4]]]> // [1, 2, 3, 4]
type T2 = Flatten<['a', ['b', 'c'], ['d']]> // ["a", "b", "c", "d"]
// 限制深度的 Flatten
type FlattenDepth<T extends any[], Depth extends number = 1> = Depth extends 0
? T
: T extends [infer First, ...infer Rest]
? First extends any[]
? [
...FlattenDepth<First, [-1, 0, 1, 2, 3, 4, 5][Depth]>,
...FlattenDepth<Rest, Depth>
]
: [First, ...FlattenDepth<Rest, Depth>]
: []
type T3 = FlattenDepth<[1, [2, [3, [4]]]], 2> // [1, 2, 3, [4]]
// 对象属性展平
type FlattenObject<T> = T extends object
? T extends infer O
? { [K in keyof O]: FlattenObject<O[K]> }
: never
: T
// 实际应用:Promise 链展平
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T
type DeepPromise = Promise<Promise<Promise<string>>>
type Unwrapped = Awaited<DeepPromise> // string62. 如何实现 UnionToIntersection 类型?
答案:
UnionToIntersection 将联合类型转换为交叉类型。
案例:
typescript
// 核心原理:利用函数参数逆变的特性
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never
// 使用示例
type T1 = UnionToIntersection<{ a: string } | { b: number }>
// { a: string } & { b: number }
type T2 = UnionToIntersection<string | number> // never(基本类型无法交叉)
// 实际应用:合并多个接口
interface FeatureA {
methodA(): void
}
interface FeatureB {
methodB(): void
}
interface FeatureC {
methodC(): void
}
type CombinedFeatures = UnionToIntersection<FeatureA | FeatureB | FeatureC>
// FeatureA & FeatureB & FeatureC
// 从数组中提取交叉类型
type TupleToIntersection<T extends any[]> = UnionToIntersection<T[number]>
type T3 = TupleToIntersection<[{ a: 1 }, { b: 2 }, { c: 3 }]>
// { a: 1 } & { b: 2 } & { c: 3 }
// 实际应用:高阶组件类型
function compose<T extends any[]>(
...fns: T
): (arg: UnionToIntersection<T[number]>) => void {
return arg => {
fns.forEach(fn => (fn as Function)(arg))
}
}63. 如何实现 Tuple 类型操作?
答案:
Tuple 类型操作包括长度获取、元素提取、追加、拼接等。
案例:
typescript
// 获取元组长度
type Length<T extends readonly any[]> = T['length']
type L1 = Length<[1, 2, 3]> // 3
type L2 = Length<[]> // 0
// 获取第一个元素
type Head<T extends readonly any[]> = T extends readonly [infer H, ...any[]]
? H
: never
type H1 = Head<[1, 2, 3]> // 1
type H2 = Head<[]> // never
// 获取剩余元素
type Tail<T extends readonly any[]> = T extends readonly [any, ...infer R]
? R
: []
type T1 = Tail<[1, 2, 3]> // [2, 3]
type T2 = Tail<[1]> // []
// 在开头添加元素
type Prepend<E, T extends any[]> = [E, ...T]
type P1 = Prepend<0, [1, 2]> // [0, 1, 2]
// 在末尾添加元素
type Append<T extends any[], E> = [...T, E]
type A1 = Append<[1, 2], 3> // [1, 2, 3]
// 反转元组
type Reverse<T extends any[], R extends any[] = []> = T extends [
infer First,
...infer Rest
]
? Reverse<Rest, [First, ...R]>
: R
type R1 = Reverse<[1, 2, 3]> // [3, 2, 1]
// 截取元组
type Take<
T extends any[],
N extends number,
R extends any[] = []
> = R['length'] extends N
? R
: T extends [infer F, ...infer Rest]
? Take<Rest, N, [...R, F]>
: R
type Tk1 = Take<[1, 2, 3, 4], 2> // [1, 2]
// 实际应用:函数柯里化
type Curry<T extends (...args: any[]) => any> = T extends (
arg: infer A,
...rest: infer R
) => infer Ret
? R extends []
? (arg: A) => Ret
: (arg: A) => Curry<(...args: R) => Ret>
: T64. 如何实现 String 类型操作?
答案:
TypeScript 4.1+ 支持模板字面量类型,可以进行字符串操作。
案例:
typescript
// 字符串拼接
type Concat<A extends string, B extends string> = `${A}${B}`;
type C1 = Concat<"Hello, ", "World">; // "Hello, World"
// 分割字符串(简化版)
type Split<
S extends string,
D extends string
> = S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];
// 获取字符串长度(有限制)
type StringLength<
S extends string,
T extends string[] = []
> = S extends `${string}${infer Rest}`
? StringLength<Rest, [...T, string]>
: T["length"];
// 实际应用:路径生成
type Path<T, P extends string = ""> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? Path<T[K], `${P}${K}.`> | `${P}${K}`
: `${P}${K}`;
}[keyof T]
: P;
interface User {
name: string;
address: {
street: string;
city: string;
};
}
type UserPath = Path<User>;
// "name" | "address" | "address.street" | "address.city"
// 事件名称生成
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type SubmitEvent = EventName<"submit">; // "onSubmit"
// CSS 变量生成
type CSSVariable<T extends string> = `--${T}`;
type PrimaryColor = CSSVariable<"primary-color">; // "--primary-color"65. 如何实现 Promise 类型工具?
答案:
Promise 类型工具用于处理异步操作的类型。
案例:
typescript
// Awaited - 提取 Promise 的返回值(TS 4.5+ 内置)
type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T
type A1 = MyAwaited<Promise<string>> // string
type A2 = MyAwaited<Promise<Promise<number>>> // number
// PromiseAll 类型
type PromiseAll<T extends readonly unknown[]> = Promise<{
-readonly [P in keyof T]: MyAwaited<T[P]>
}>
// 使用示例
const promises = [
Promise.resolve(1),
Promise.resolve('hello'),
Promise.resolve(true)
] as const
type Result = PromiseAll<typeof promises> // Promise<[number, string, boolean]>
// 异步函数返回类型
type AsyncReturnType<T extends (...args: any[]) => Promise<any>> = MyAwaited<
ReturnType<T>
>
async function fetchUser(): Promise<{ id: number; name: string }> {
return { id: 1, name: 'Tom' }
}
type UserType = AsyncReturnType<typeof fetchUser> // { id: number; name: string }
// Promise 化函数类型
type Promisify<T extends (...args: any[]) => any> = (
...args: Parameters<T>
) => Promise<ReturnType<T>>
function syncFn(a: number, b: number): number {
return a + b
}
type AsyncFn = Promisify<typeof syncFn> // (a: number, b: number) => Promise<number>
// 实际应用:API 客户端类型
interface ApiClient {
get<T>(url: string): Promise<T>
post<T>(url: string, body: any): Promise<T>
put<T>(url: string, body: any): Promise<T>
delete<T>(url: string): Promise<T>
}
type ApiResponse<T> = Promise<{
data: T
status: number
message: string
}>66. 如何实现 EventEmitter 类型?
答案:
EventEmitter 类型用于类型安全的事件系统。
案例:
typescript
// 类型安全的事件发射器
interface EventMap {
[event: string]: any
}
type EventKey<T extends EventMap> = string & keyof T
type EventReceiver<T> = (params: T) => void
interface TypedEventEmitter<Events extends EventMap> {
on<E extends EventKey<Events>>(
event: E,
listener: EventReceiver<Events[E]>
): this
off<E extends EventKey<Events>>(
event: E,
listener: EventReceiver<Events[E]>
): this
emit<E extends EventKey<Events>>(event: E, params: Events[E]): boolean
once<E extends EventKey<Events>>(
event: E,
listener: EventReceiver<Events[E]>
): this
}
// 使用示例
interface MyEvents {
userLogin: { userId: string; timestamp: number }
userLogout: { userId: string }
messageReceived: { from: string; content: string }
}
class MyEventEmitter implements TypedEventEmitter<MyEvents> {
private listeners: { [K in keyof MyEvents]?: EventReceiver<MyEvents[K]>[] } =
{}
on<E extends keyof MyEvents>(
event: E,
listener: EventReceiver<MyEvents[E]>
): this {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event]!.push(listener)
return this
}
off<E extends keyof MyEvents>(
event: E,
listener: EventReceiver<MyEvents[E]>
): this {
const arr = this.listeners[event]
if (arr) {
const idx = arr.indexOf(listener)
if (idx !== -1) arr.splice(idx, 1)
}
return this
}
emit<E extends keyof MyEvents>(event: E, params: MyEvents[E]): boolean {
const arr = this.listeners[event]
if (arr && arr.length > 0) {
arr.forEach(listener => listener(params))
return true
}
return false
}
once<E extends keyof MyEvents>(
event: E,
listener: EventReceiver<MyEvents[E]>
): this {
const onceListener = (params: MyEvents[E]) => {
this.off(event, onceListener as EventReceiver<MyEvents[E]>)
listener(params)
}
return this.on(event, onceListener as EventReceiver<MyEvents[E]>)
}
}
const emitter = new MyEventEmitter()
emitter.on('userLogin', ({ userId, timestamp }) => {
console.log(`User ${userId} logged in at ${timestamp}`)
})
emitter.emit('userLogin', { userId: '123', timestamp: Date.now() })67. 如何实现 API 响应类型?
答案:
API 响应类型用于标准化 API 返回数据的类型结构。
案例:
typescript
// 基础 API 响应类型
interface ApiResponse<T> {
data: T
status: number
message: string
timestamp: string
}
interface ApiError {
code: string
message: string
details?: Record<string, string[]>
}
interface ApiErrorResponse {
error: ApiError
status: number
timestamp: string
}
// 分页响应类型
interface PaginatedResponse<T> {
data: T[]
pagination: {
page: number
pageSize: number
total: number
totalPages: number
}
}
// 使用示例
interface User {
id: string
name: string
email: string
}
type GetUsersResponse = ApiResponse<PaginatedResponse<User>>
// 类型守卫
function isApiResponse<T>(response: any): response is ApiResponse<T> {
return response && 'data' in response && 'status' in response
}
function isApiError(response: any): response is ApiErrorResponse {
return response && 'error' in response
}
// 实际应用:API 客户端
class ApiClient {
async request<T>(
url: string,
options?: RequestInit
): Promise<ApiResponse<T>> {
const response = await fetch(url, options)
const data = await response.json()
if (!response.ok) {
throw new Error(isApiError(data) ? data.error.message : 'Unknown error')
}
return data as ApiResponse<T>
}
async get<T>(url: string): Promise<T> {
const response = await this.request<T>(url)
return response.data
}
async getPaginated<T>(url: string): Promise<PaginatedResponse<T>> {
const response = await this.request<PaginatedResponse<T>>(url)
return response.data
}
}
// 使用
const client = new ApiClient()
const users = await client.getPaginated<User>('/api/users')
console.log(users.data, users.pagination)68. 如何实现 Form 表单类型?
答案:
表单类型用于类型安全的表单处理和验证。
案例:
typescript
// 表单字段类型
type FormField<T> = {
value: T
error?: string
touched: boolean
valid: boolean
}
// 表单状态类型
type FormState<T> = {
[K in keyof T]: FormField<T[K]>
}
// 表单配置类型
type FormConfig<T> = {
[K in keyof T]: {
initialValue: T[K]
validators?: Array<(value: T[K]) => string | undefined>
}
}
// 使用示例
interface LoginForm {
email: string
password: string
rememberMe: boolean
}
const loginFormConfig: FormConfig<LoginForm> = {
email: {
initialValue: '',
validators: [
v => (!v ? 'Email is required' : undefined),
v => (!v.includes('@') ? 'Invalid email' : undefined)
]
},
password: {
initialValue: '',
validators: [
v => (!v ? 'Password is required' : undefined),
v => (v.length < 8 ? 'Password must be at least 8 characters' : undefined)
]
},
rememberMe: {
initialValue: false
}
}
// 表单 Hook 类型
interface UseFormReturn<T> {
values: T
errors: Partial<Record<keyof T, string>>
touched: Partial<Record<keyof T, boolean>>
isValid: boolean
isSubmitting: boolean
setValue: <K extends keyof T>(field: K, value: T[K]) => void
handleSubmit: (
onSubmit: (values: T) => Promise<void>
) => (e: FormEvent) => void
}
// 实际应用:类型安全的表单组件
interface FormProps<T> {
initialValues: T
onSubmit: (values: T) => Promise<void>
children: (props: UseFormReturn<T>) => React.ReactNode
}
function Form<T extends Record<string, any>>({
initialValues,
onSubmit,
children
}: FormProps<T>) {
// 实现...
return null
}69. 如何实现 Router 路由类型?
答案:
路由类型用于类型安全的路由导航和参数传递。
案例:
typescript
// 路由配置类型
interface RouteConfig {
path: string
component: React.ComponentType
exact?: boolean
children?: RouteConfig[]
}
// 类型安全的路由参数
type RouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param | keyof RouteParams<Rest>]: string }
: T extends `${string}:${infer Param}`
? { [K in Param]: string }
: {}
// 使用示例
type UserParams = RouteParams<'/users/:userId/posts/:postId'>
// { userId: string; postId: string }
// 路由名称到路径的映射
type RoutePaths = {
home: '/'
about: '/about'
user: '/users/:userId'
userPosts: '/users/:userId/posts'
post: '/users/:userId/posts/:postId'
}
// 生成路径函数
type GeneratePath<T extends Record<string, string>> = {
[K in keyof T]: T[K] extends `${string}:${string}`
? (params: RouteParams<T[K]>) => string
: () => string
}
// 实际应用:类型安全的路由导航
interface TypedHistory<Paths extends Record<string, string>> {
push<K extends keyof Paths>(
path: K,
...args: Paths[K] extends `${string}:${string}`
? [RouteParams<Paths[K]>]
: []
): void
replace<K extends keyof Paths>(
path: K,
...args: Paths[K] extends `${string}:${string}`
? [RouteParams<Paths[K]>]
: []
): void
}
// 使用示例
const paths = {
home: '/',
user: '/users/:userId'
} as const
type MyHistory = TypedHistory<typeof paths>
// history.push("home"); // ✅
// history.push("user", { userId: "123" }); // ✅
// history.push("user"); // ❌ 缺少参数70. 如何实现 Redux State 类型?
答案:
Redux State 类型用于类型安全的状态管理。
案例:
typescript
// Action 类型
interface Action<T extends string = string, P = any> {
type: T
payload?: P
}
// 类型安全的 Action Creator
type ActionCreator<T extends string, P, Args extends any[]> = (
...args: Args
) => Action<T, P>
// Reducer 类型
type Reducer<S, A extends Action> = (state: S | undefined, action: A) => S
// Store 类型
interface Store<S, A extends Action> {
getState(): S
dispatch(action: A): A
subscribe(listener: () => void): () => void
}
// 使用示例
// State 定义
interface AppState {
auth: {
user: { id: string; name: string } | null
isLoading: boolean
error: string | null
}
posts: {
items: Array<{ id: string; title: string; content: string }>
loading: boolean
}
}
// Action 类型定义
const LOGIN_REQUEST = 'auth/LOGIN_REQUEST' as const
const LOGIN_SUCCESS = 'auth/LOGIN_SUCCESS' as const
const LOGIN_FAILURE = 'auth/LOGIN_FAILURE' as const
interface LoginRequestAction extends Action<typeof LOGIN_REQUEST> {}
interface LoginSuccessAction extends Action<
typeof LOGIN_SUCCESS,
{ id: string; name: string }
> {}
interface LoginFailureAction extends Action<typeof LOGIN_FAILURE, string> {}
type AuthAction = LoginRequestAction | LoginSuccessAction | LoginFailureAction
// Action Creators
const loginRequest = (): LoginRequestAction => ({
type: LOGIN_REQUEST
})
const loginSuccess = (user: {
id: string
name: string
}): LoginSuccessAction => ({
type: LOGIN_SUCCESS,
payload: user
})
const loginFailure = (error: string): LoginFailureAction => ({
type: LOGIN_FAILURE,
payload: error
})
// Reducer
const authReducer: Reducer<AppState['auth'], AuthAction> = (
state = { user: null, isLoading: false, error: null },
action
) => {
switch (action.type) {
case LOGIN_REQUEST:
return { ...state, isLoading: true, error: null }
case LOGIN_SUCCESS:
return { ...state, user: action.payload, isLoading: false }
case LOGIN_FAILURE:
return { ...state, error: action.payload, isLoading: false }
default:
return state
}
}
// Selector 类型
type Selector<S, R> = (state: S) => R
const selectUser: Selector<AppState, AppState['auth']['user']> = state =>
state.auth.user
const selectIsAuthenticated: Selector<AppState, boolean> = state =>
!!state.auth.user第71-85题:工程化与配置
71. tsconfig.json 常用配置有哪些?
答案:
tsconfig.json 是 TypeScript 项目的配置文件,控制编译行为和类型检查规则。
案例:
json
{
"compilerOptions": {
// 基础配置
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
// 输出配置
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationDir": "./types",
"sourceMap": true,
// 模块解析
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"]
},
"resolveJsonModule": true,
// 严格类型检查
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
// 额外检查
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
// 高级配置
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}72. 如何配置 TypeScript 路径别名?
答案:
路径别名简化模块导入,避免复杂的相对路径。
案例:
json
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"],
"@hooks/*": ["src/hooks/*"],
"@types/*": ["src/types/*"],
"@api/*": ["src/api/*"]
}
}
}typescript
// 使用前
import { Button } from '../../../../components/Button'
import { useAuth } from '../../../hooks/useAuth'
// 使用后
import { Button } from '@components/Button'
import { useAuth } from '@hooks/useAuth'Vite 配置:
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils')
}
}
})Webpack 配置:
javascript
// webpack.config.js
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components')
}
}
}73. 如何配置 TypeScript 与 ESLint?
答案:
TypeScript ESLint 提供对 TypeScript 的 lint 支持。
案例:
json
// .eslintrc.json
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ "argsIgnorePattern": "^_" }
],
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": "error",
"@typescript-eslint/strict-boolean-expressions": "error"
}
}package.json 脚本:
json
{
"scripts": {
"lint": "eslint . --ext .ts,.tsx",
"lint:fix": "eslint . --ext .ts,.tsx --fix",
"type-check": "tsc --noEmit"
}
}74. 如何配置 TypeScript 与 Prettier?
答案:
Prettier 负责代码格式化,与 ESLint 配合使用。
案例:
json
// .prettierrc
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf"
}json
// .prettierignore
node_modules
dist
build
coverage
*.log与 ESLint 集成:
json
// .eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier", // 禁用与 Prettier 冲突的规则
"plugin:prettier/recommended"
]
}package.json:
json
{
"devDependencies": {
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"prettier": "^3.0.0"
},
"scripts": {
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\""
}
}75. 如何配置 TypeScript 与 Jest?
答案:
Jest 配合 ts-jest 或 @swc/jest 进行 TypeScript 测试。
案例:
json
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
roots: ["<rootDir>/src"],
testMatch: ["**/__tests__/**/*.test.ts"],
transform: {
"^.+\\.tsx?$": "ts-jest"
},
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/src/$1"
},
collectCoverageFrom: [
"src/**/*.{ts,tsx}",
"!src/**/*.d.ts"
],
coverageThreshold: {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
};typescript
// sum.ts
export function sum(a: number, b: number): number {
return a + b
}
// sum.test.ts
import { sum } from './sum'
describe('sum', () => {
it('should add two numbers', () => {
expect(sum(1, 2)).toBe(3)
})
it('should handle negative numbers', () => {
expect(sum(-1, -2)).toBe(-3)
})
})76. 如何配置 TypeScript 与 Vite?
答案:
Vite 原生支持 TypeScript,配置简单高效。
案例:
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom']
}
}
}
},
server: {
port: 3000,
open: true
}
})json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}77. 如何配置 TypeScript 与 Webpack?
答案:
Webpack 需要 ts-loader 或 babel-loader 处理 TypeScript。
案例:
javascript
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
],
devServer: {
static: {
directory: path.join(__dirname, 'public')
},
compress: true,
port: 9000
}
}Babel 方案(更快):
javascript
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
'@babel/preset-react'
]
}
}
}
]
}
}78. 如何配置 TypeScript 与 Node.js?
答案:
Node.js 项目需要合适的 tsconfig 和运行配置。
案例:
json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}json
// package.json
{
"name": "my-node-app",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx watch src/index.ts",
"type-check": "tsc --noEmit"
},
"devDependencies": {
"@types/node": "^20.0.0",
"tsx": "^4.0.0",
"typescript": "^5.0.0"
}
}typescript
// src/index.ts
import http from 'http'
import { config } from './config'
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ message: 'Hello from TypeScript!' }))
})
server.listen(config.port, () => {
console.log(`Server running on port ${config.port}`)
})79. 如何配置 TypeScript monorepo?
答案:
Monorepo 使用 Project References 管理多个包。
案例:
my-monorepo/
├── packages/
│ ├── shared/
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── app/
│ ├── src/
│ ├── package.json
│ └── tsconfig.json
├── tsconfig.json
└── package.jsonjson
// 根 tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"references": [{ "path": "./packages/shared" }, { "path": "./packages/app" }]
}json
// packages/shared/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"composite": true
},
"include": ["src/**/*"]
}json
// packages/app/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"references": [{ "path": "../shared" }],
"include": ["src/**/*"]
}80. 如何配置 TypeScript 声明文件?
答案:
声明文件(.d.ts)为 JavaScript 库提供类型定义。
案例:
typescript
// types/my-lib.d.ts
declare module 'my-lib' {
export interface Config {
apiKey: string
timeout?: number
}
export class Client {
constructor(config: Config)
get<T>(url: string): Promise<T>
post<T>(url: string, data: unknown): Promise<T>
}
export function createClient(config: Config): Client
}typescript
// 全局声明
declare global {
interface Window {
myApp: {
version: string
config: Record<string, unknown>
}
}
const __DEV__: boolean
const __VERSION__: string
}
export {} // 使文件成为模块typescript
// 扩展第三方库
declare module 'express' {
interface Request {
user?: {
id: string
email: string
}
}
}json
// package.json
{
"name": "my-package",
"types": "dist/index.d.ts",
"files": ["dist"]
}81. 如何配置 TypeScript 严格模式?
答案:
严格模式启用所有严格类型检查选项,提高代码质量。
案例:
json
{
"compilerOptions": {
"strict": true,
// 等价于启用以下所有选项:
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
// 额外的严格检查(需手动启用)
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}严格模式示例:
typescript
// strictNullChecks
function greet(name: string | null) {
console.log(name.toUpperCase()) // ❌ 错误:可能为 null
if (name) {
console.log(name.toUpperCase()) // ✅
}
}
// strictFunctionTypes
interface Comparator {
compare(a: string, b: string): number
}
const compare: Comparator = {
compare: (a, b) => a.localeCompare(b) // ✅
}
// strictPropertyInitialization
class User {
name: string // ❌ 错误:未初始化
age: number = 0 // ✅
constructor(name: string) {
this.name = name
}
}82. 如何配置 TypeScript 增量编译?
答案:
增量编译只重新编译修改的文件,提高构建速度。
案例:
json
{
"compilerOptions": {
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo",
"composite": true, // 项目引用必需
"declaration": true,
"declarationMap": true
}
}构建缓存:
json
// package.json
{
"scripts": {
"build": "tsc --build",
"build:force": "tsc --build --force",
"build:watch": "tsc --build --watch",
"clean": "tsc --build --clean"
}
}性能优化技巧:
json
{
"compilerOptions": {
"skipLibCheck": true, // 跳过声明文件类型检查
"isolatedModules": true, // 确保文件可独立编译
"preserveSymlinks": true // 保留符号链接
},
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
}83. 如何配置 TypeScript 与 Docker?
答案:
Docker 中运行 TypeScript 需要多阶段构建优化。
案例:
dockerfile
# Dockerfile
# 阶段1:构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
# 阶段2:运行
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- NODE_ENV=production
volumes:
- ./logs:/app/logs开发环境配置:
dockerfile
# Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]84. 如何配置 TypeScript 与 CI/CD?
答案:
CI/CD 中运行类型检查和测试确保代码质量。
案例:
yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Test
run: npm run test -- --coverage
- name: Build
run: npm run build
- name: Upload coverage
uses: codecov/codecov-action@v3GitLab CI:
yaml
# .gitlab-ci.yml
stages:
- test
- build
- deploy
test:
stage: test
image: node:20
script:
- npm ci
- npm run type-check
- npm run lint
- npm run test
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
build:
stage: build
image: node:20
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/85. 如何配置 TypeScript 性能优化?
答案:
多种策略可以优化 TypeScript 编译和运行时性能。
案例:
json
// tsconfig.json 优化
{
"compilerOptions": {
// 编译速度优化
"incremental": true,
"skipLibCheck": true,
"isolatedModules": true,
// 输出优化
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
// 代码质量
"strict": true,
"noUnusedLocals": true,
"noImplicitReturns": true
},
"exclude": [
"node_modules",
"dist",
"**/*.test.ts",
"**/*.spec.ts",
"coverage"
]
}构建工具选择:
typescript
// Vite - 开发最快
// esbuild - 编译最快(Go 编写)
// swc - Rust 编写,比 tsc 快 20 倍
// 使用 swc 替代 ts-node
// npm install -D @swc/core @swc/cli
// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"target": "es2020"
},
"module": {
"type": "es6"
}
}运行时优化:
typescript
// 使用 const 断言减少运行时开销
const config = {
api: 'https://api.example.com',
timeout: 5000
} as const
// 使用枚举替代对象(编译时优化)
enum Status {
Pending,
Success,
Error
}
// 类型导入使用 import type
import type { User } from './types'
// 确保类型在编译后完全移除第86-100题:实践应用与设计原则
86. TypeScript 在 React 中的最佳实践?
答案:
React 配合 TypeScript 提供组件类型安全和更好的开发体验。
案例:
typescript
// 组件 Props 类型
interface ButtonProps {
variant?: "primary" | "secondary" | "danger";
size?: "sm" | "md" | "lg";
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}
// FC 类型(不推荐,会隐式包含 children)
// const Button: React.FC<ButtonProps> = ({ children }) => <button>{children}</button>;
// 推荐:直接定义函数组件
function Button({
variant = "primary",
size = "md",
disabled = false,
onClick,
children
}: ButtonProps): JSX.Element {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
}
// 泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
keyExtractor: (item: T) => string | number;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>): JSX.Element {
return (
<ul>
{items.map(item => (
<li key={keyExtractor(item)}>{renderItem(item)}</li>
))}
</ul>
);
}
// 使用
interface User {
id: string;
name: string;
}
<List<User>
items={users}
renderItem={(user) => <span>{user.name}</span>}
keyExtractor={(user) => user.id}
/>
// 自定义 Hooks
type UseToggleReturn = [boolean, () => void];
function useToggle(initial = false): UseToggleReturn {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []);
return [value, toggle];
}
// Context 类型
interface ThemeContextType {
theme: "light" | "dark";
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within ThemeProvider");
}
return context;
}87. TypeScript 在 Vue 中的最佳实践?
答案:
Vue 3 配合 TypeScript 提供完整的类型支持。
案例:
typescript
// 组合式 API
<script setup lang="ts">
interface User {
id: number;
name: string;
email: string;
}
// 响应式数据类型推断
const count = ref<number>(0);
const user = ref<User | null>(null);
const users = ref<User[]>([]);
// 计算属性
const doubleCount = computed<number>(() => count.value * 2);
// 方法
function increment(): void {
count.value++;
}
async function fetchUser(id: number): Promise<void> {
const response = await fetch(`/api/users/${id}`);
user.value = await response.json() as User;
}
// Props 类型定义
interface Props {
title: string;
modelValue?: string;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: ""
});
// Emits 类型定义
const emit = defineEmits<{
(e: "update:modelValue", value: string): void;
(e: "submit", data: FormData): void;
}>();
// 自定义 Composable
function useCounter(initial = 0) {
const count = ref(initial);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count: readonly(count),
increment,
decrement
};
}
// 使用
const { count, increment } = useCounter(10);
</script>88. TypeScript 在 Node.js 中的最佳实践?
答案:
Node.js 配合 TypeScript 提供服务端类型安全。
案例:
typescript
// Express 类型安全
import express, { Request, Response, NextFunction } from 'express'
interface AuthenticatedRequest extends Request {
user?: {
id: string
email: string
}
}
interface CreateUserRequest {
email: string
password: string
name: string
}
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
}
// 中间件类型
const authMiddleware = (
req: AuthenticatedRequest,
res: Response,
next: NextFunction
): void => {
const token = req.headers.authorization
if (!token) {
res.status(401).json({ success: false, error: 'Unauthorized' })
return
}
// 验证 token...
req.user = { id: '123', email: 'user@example.com' }
next()
}
// 控制器类型
const createUser = async (
req: Request<{}, ApiResponse<{ id: string }>, CreateUserRequest>,
res: Response<ApiResponse<{ id: string }>>
): Promise<void> => {
try {
const { email, password, name } = req.body
const user = await UserService.create({ email, password, name })
res.status(201).json({ success: true, data: { id: user.id } })
} catch (error) {
res.status(400).json({ success: false, error: error.message })
}
}
// 服务层类型
class UserService {
static async create(data: CreateUserRequest): Promise<User> {
// 实现...
}
static async findById(id: string): Promise<User | null> {
// 实现...
}
}
// 错误处理类型
class AppError extends Error {
constructor(
message: string,
public statusCode: number,
public code: string
) {
super(message)
}
}
const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
): void => {
if (err instanceof AppError) {
res.status(err.statusCode).json({
success: false,
error: err.message,
code: err.code
})
return
}
res.status(500).json({
success: false,
error: 'Internal server error'
})
}89. TypeScript 设计模式有哪些?
答案:
TypeScript 支持经典设计模式,增强代码可维护性。
案例:
typescript
// 单例模式
class Singleton {
private static instance: Singleton
private constructor() {}
static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton()
}
return Singleton.instance
}
}
// 工厂模式
interface Animal {
speak(): string
}
class Dog implements Animal {
speak() {
return 'Woof!'
}
}
class Cat implements Animal {
speak() {
return 'Meow!'
}
}
class AnimalFactory {
static create(type: 'dog' | 'cat'): Animal {
switch (type) {
case 'dog':
return new Dog()
case 'cat':
return new Cat()
default:
throw new Error('Unknown animal type')
}
}
}
// 观察者模式
interface Observer<T> {
update(data: T): void
}
interface Subject<T> {
attach(observer: Observer<T>): void
detach(observer: Observer<T>): void
notify(data: T): void
}
class ConcreteSubject<T> implements Subject<T> {
private observers: Observer<T>[] = []
attach(observer: Observer<T>): void {
this.observers.push(observer)
}
detach(observer: Observer<T>): void {
const index = this.observers.indexOf(observer)
if (index !== -1) {
this.observers.splice(index, 1)
}
}
notify(data: T): void {
this.observers.forEach(observer => observer.update(data))
}
}
// 策略模式
interface PaymentStrategy {
pay(amount: number): void
}
class CreditCardPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`Paid ${amount} using Credit Card`)
}
}
class PayPalPayment implements PaymentStrategy {
pay(amount: number): void {
console.log(`Paid ${amount} using PayPal`)
}
}
class ShoppingCart {
private strategy: PaymentStrategy
constructor(strategy: PaymentStrategy) {
this.strategy = strategy
}
checkout(amount: number): void {
this.strategy.pay(amount)
}
}
// 装饰器模式
function log<T extends (...args: any[]) => any>(
fn: T
): (...args: Parameters<T>) => ReturnType<T> {
return (...args) => {
console.log(`Calling ${fn.name} with`, args)
const result = fn(...args)
console.log(`Result:`, result)
return result
}
}
// 依赖注入
interface Database {
query(sql: string): any[]
}
class UserRepository {
constructor(private db: Database) {}
findAll() {
return this.db.query('SELECT * FROM users')
}
}90. TypeScript 如何进行类型测试?
答案:
类型测试验证类型系统的行为是否符合预期。
案例:
typescript
// 使用 expect-type 库
import {
expectType,
expectError,
expectAssignable,
expectNotAssignable
} from 'tsd'
// 测试类型相等
expectType<string>('hello')
expectType<number>(42)
// 测试类型错误
expectError<string>(42)
// 测试类型可赋值性
expectAssignable<string | number>('hello')
expectNotAssignable<string>(42)
// 自定义类型测试工具
type AssertEqual<T, U> = [T] extends [U]
? [U] extends [T]
? true
: false
: false
// 测试用例
type Test1 = AssertEqual<string, string> // true
type Test2 = AssertEqual<string, number> // false
type Test3 = AssertEqual<{ a: string }, { a: string }> // true
// 测试泛型
type DeepPartialTest = AssertEqual<
DeepPartial<{ a: { b: string } }>,
{ a?: { b?: string } }
> // true
// 使用 vitest 进行类型测试
import { describe, it, assertType } from 'vitest'
describe('Type tests', () => {
it('should have correct return type', () => {
const result = add(1, 2)
assertType<number>(result)
})
it('should accept correct props', () => {
const props = { name: 'John', age: 30 }
assertType<UserProps>(props)
})
})
// 编译时类型检查
// @ts-expect-error - 下一行应该有类型错误
const x: string = 42
// @ts-ignore - 忽略下一行的类型错误
const y: string = 4291. TypeScript 如何进行代码重构?
答案:
TypeScript 的类型系统支持安全的代码重构。
案例:
typescript
// 重命名符号(IDE 支持)
// F2 重命名变量、函数、类名
// 提取函数
// 重构前
function processUserData(user: User) {
const fullName = `${user.firstName} ${user.lastName}`
const age = calculateAge(user.birthDate)
return { fullName, age }
}
// 重构后
function getFullName(user: User): string {
return `${user.firstName} ${user.lastName}`
}
function processUserData(user: User): ProcessedUser {
return {
fullName: getFullName(user),
age: calculateAge(user.birthDate)
}
}
// 提取接口
// 重构前
function createUser(name: string, email: string, age: number): User {
return { name, email, age }
}
// 重构后
interface CreateUserInput {
name: string
email: string
age: number
}
function createUser(input: CreateUserInput): User {
return { ...input, id: generateId() }
}
// 类型别名提取
type UserId = string
type Email = string
type Timestamp = number
interface User {
id: UserId
email: Email
createdAt: Timestamp
}
// 使用 branded types 防止 ID 混淆
type UserId = string & { __brand: 'UserId' }
type PostId = string & { __brand: 'PostId' }
function getUser(id: UserId): User {
// 实现
}
function getPost(id: PostId): Post {
// 实现
}
const userId = '123' as UserId
const postId = '456' as PostId
getUser(userId) // ✅
getUser(postId) // ❌ 类型错误92. TypeScript 如何进行性能监控?
答案:
TypeScript 编译性能可以通过多种方式监控和优化。
案例:
json
// 生成性能报告
{
"compilerOptions": {
"generateTrace": "./trace",
"extendedDiagnostics": true
}
}bash
# 查看编译性能
tsc --extendedDiagnostics
# 输出示例:
# Files: 523
# Lines: 125430
# Nodes: 892341
# Identifiers: 234567
# Symbols: 456789
# Types: 123456
# Memory used: 234567K
# Parse time: 1.23s
# Bind time: 0.45s
# Check time: 3.67s
# Emit time: 0.89s
# Total time: 6.24stypescript
// 运行时性能监控
interface PerformanceMetrics {
operation: string
duration: number
timestamp: number
}
class PerformanceMonitor {
private metrics: PerformanceMetrics[] = []
measure<T>(name: string, fn: () => T): T {
const start = performance.now()
const result = fn()
const duration = performance.now() - start
this.metrics.push({
operation: name,
duration,
timestamp: Date.now()
})
return result
}
async measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T> {
const start = performance.now()
const result = await fn()
const duration = performance.now() - start
this.metrics.push({
operation: name,
duration,
timestamp: Date.now()
})
return result
}
getReport(): PerformanceMetrics[] {
return [...this.metrics]
}
}
const monitor = new PerformanceMonitor()
// 使用
const result = monitor.measure('heavy computation', () => {
// 耗时操作
return compute()
})93. TypeScript 如何进行错误处理?
答案:
TypeScript 提供多种错误处理策略。
案例:
typescript
// Result 类型(函数式错误处理)
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E }
function divide(a: number, b: number): Result<number, string> {
if (b === 0) {
return { success: false, error: 'Cannot divide by zero' }
}
return { success: true, data: a / b }
}
const result = divide(10, 2)
if (result.success) {
console.log(result.data) // 5
} else {
console.error(result.error)
}
// Option 类型
type Option<T> = { type: 'Some'; value: T } | { type: 'None' }
function findUser(id: string): Option<User> {
const user = users.find(u => u.id === id)
return user ? { type: 'Some', value: user } : { type: 'None' }
}
// 自定义错误类
class ValidationError extends Error {
constructor(
message: string,
public field: string,
public code: string
) {
super(message)
this.name = 'ValidationError'
}
}
class NotFoundError extends Error {
constructor(
message: string,
public resource: string,
public id: string
) {
super(message)
this.name = 'NotFoundError'
}
}
// 错误边界(React)
interface Props {
children: React.ReactNode
fallback: React.ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends React.Component<Props, State> {
state: State = { hasError: false }
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
console.error('Error caught by boundary:', error, errorInfo)
}
render(): React.ReactNode {
if (this.state.hasError) {
return this.props.fallback
}
return this.props.children
}
}94. TypeScript 如何进行单元测试?
答案:
TypeScript 配合 Jest/Vitest 进行类型安全的单元测试。
案例:
typescript
// calculator.ts
export class Calculator {
add(a: number, b: number): number {
return a + b
}
divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Cannot divide by zero')
}
return a / b
}
}
// calculator.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { Calculator } from './calculator'
describe('Calculator', () => {
let calculator: Calculator
beforeEach(() => {
calculator = new Calculator()
})
describe('add', () => {
it('should add two positive numbers', () => {
expect(calculator.add(2, 3)).toBe(5)
})
it('should add negative numbers', () => {
expect(calculator.add(-2, -3)).toBe(-5)
})
it('should handle zero', () => {
expect(calculator.add(0, 5)).toBe(5)
})
})
describe('divide', () => {
it('should divide two numbers', () => {
expect(calculator.divide(10, 2)).toBe(5)
})
it('should throw error when dividing by zero', () => {
expect(() => calculator.divide(10, 0)).toThrow('Cannot divide by zero')
})
})
})
// Mock 类型
import { vi } from 'vitest'
interface Database {
query(sql: string): Promise<any[]>
}
const mockDb: Database = {
query: vi.fn().mockResolvedValue([{ id: 1, name: 'Test' }])
}
// 类型测试
import { expectType } from 'tsd'
describe('Type tests', () => {
it('should return correct type', () => {
const result = calculator.add(1, 2)
expectType<number>(result)
})
})95. TypeScript 如何进行 API 设计?
答案:
类型优先的 API 设计提高代码质量和可维护性。
案例:
typescript
// API 版本控制
namespace V1 {
export interface User {
id: string
name: string
}
}
namespace V2 {
export interface User {
id: string
firstName: string
lastName: string
email: string
}
}
// 类型安全的 API 客户端
interface ApiClientConfig {
baseURL: string
headers?: Record<string, string>
timeout?: number
}
interface RequestConfig<T = unknown> {
params?: Record<string, string>
data?: T
headers?: Record<string, string>
}
interface ApiResponse<T> {
data: T
status: number
headers: Record<string, string>
}
class ApiClient {
constructor(private config: ApiClientConfig) {}
async get<T>(url: string, config?: RequestConfig): Promise<ApiResponse<T>> {
return this.request<T>('GET', url, config)
}
async post<T, D = unknown>(
url: string,
config?: RequestConfig<D>
): Promise<ApiResponse<T>> {
return this.request<T>('POST', url, config)
}
private async request<T>(
method: string,
url: string,
config?: RequestConfig
): Promise<ApiResponse<T>> {
// 实现...
}
}
// 资源特定客户端
interface User {
id: string
name: string
email: string
}
interface CreateUserInput {
name: string
email: string
}
interface UpdateUserInput {
name?: string
email?: string
}
class UserApi {
constructor(private client: ApiClient) {}
async getAll(): Promise<User[]> {
const response = await this.client.get<User[]>('/users')
return response.data
}
async getById(id: string): Promise<User> {
const response = await this.client.get<User>(`/users/${id}`)
return response.data
}
async create(input: CreateUserInput): Promise<User> {
const response = await this.client.post<User, CreateUserInput>('/users', {
data: input
})
return response.data
}
async update(id: string, input: UpdateUserInput): Promise<User> {
const response = await this.client.patch<User, UpdateUserInput>(
`/users/${id}`,
{ data: input }
)
return response.data
}
}96. TypeScript 如何进行数据库操作?
答案:
TypeScript 配合 ORM 提供类型安全的数据库操作。
案例:
typescript
// Prisma ORM 示例
// schema.prisma
model User {
id String @id @default(uuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id String @id @default(uuid())
title String
content String?
author User @relation(fields: [authorId], references: [id])
authorId String
}
// 生成的类型
import { PrismaClient, User, Post } from "@prisma/client";
const prisma = new PrismaClient();
// 类型安全的查询
async function getUserWithPosts(userId: string): Promise<
User & { posts: Post[] }
> {
return prisma.user.findUnique({
where: { id: userId },
include: { posts: true }
});
}
// 类型安全的创建
async function createUser(data: {
email: string;
name?: string;
}): Promise<User> {
return prisma.user.create({ data });
}
// Drizzle ORM 示例
import { pgTable, serial, varchar, text, timestamp } from "drizzle-orm/pg-core";
import { eq } from "drizzle-orm";
const users = pgTable("users", {
id: serial("id").primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
name: varchar("name", { length: 255 }),
createdAt: timestamp("created_at").defaultNow()
});
type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;
// 类型安全的查询
async function getUserByEmail(email: string): Promise<User | undefined> {
return db.select().from(users).where(eq(users.email, email)).get();
}97. TypeScript 如何进行状态管理?
答案:
TypeScript 配合状态管理库提供类型安全的状态操作。
案例:
typescript
// Zustand 类型安全状态管理
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
interface User {
id: string;
name: string;
email: string;
}
interface AuthState {
user: User | null;
isLoading: boolean;
error: string | null;
}
interface AuthActions {
login: (email: string, password: string) => Promise<void>;
logout: () => void;
setUser: (user: User | null) => void;
}
type AuthStore = AuthState & AuthActions;
const useAuthStore = create<AuthStore>()(
devtools(
persist(
(set) => ({
user: null,
isLoading: false,
error: null,
login: async (email, password) => {
set({ isLoading: true, error: null });
try {
const user = await authApi.login(email, password);
set({ user, isLoading: false });
} catch (error) {
set({ error: error.message, isLoading: false });
}
},
logout: () => {
set({ user: null });
},
setUser: (user) => {
set({ user });
}
}),
{
name: "auth-storage",
partialize: (state) => ({ user: state.user })
}
)
)
);
// 使用
function UserProfile() {
const { user, logout } = useAuthStore();
if (!user) return <div>Not logged in</div>;
return (
<div>
<h1>{user.name}</h1>
<button onClick={logout}>Logout</button>
</div>
);
}98. TypeScript 如何进行微服务通信?
答案:
TypeScript 支持多种微服务通信模式。
案例:
typescript
// gRPC 类型定义
// user.proto
syntax = "proto3";
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc CreateUser (CreateUserRequest) returns (User);
}
message GetUserRequest {
string id = 1;
}
message CreateUserRequest {
string email = 1;
string name = 2;
}
message User {
string id = 1;
string email = 2;
string name = 3;
}
// 生成的 TypeScript 类型
interface UserService {
getUser(request: GetUserRequest): Promise<User>;
createUser(request: CreateUserRequest): Promise<User>;
}
// GraphQL 类型
// schema.graphql
type User {
id: ID!
email: String!
name: String
}
type Query {
user(id: ID!): User
users: [User!]!
}
type Mutation {
createUser(email: String!, name: String): User!
}
// 生成的 TypeScript 类型
interface Query {
user(args: { id: string }): Promise<User | null>;
users(): Promise<User[]>;
}
interface Mutation {
createUser(args: { email: string; name?: string }): Promise<User>;
}
// 消息队列类型
interface Message<T> {
id: string;
type: string;
payload: T;
timestamp: number;
}
interface EventBus {
publish<T>(topic: string, message: Message<T>): Promise<void>;
subscribe<T>(
topic: string,
handler: (message: Message<T>) => void
): () => void;
}
// 使用
interface UserCreatedEvent {
userId: string;
email: string;
}
eventBus.subscribe<UserCreatedEvent>("user.created", (message) => {
console.log(`User ${message.payload.userId} created`);
});99. TypeScript 如何进行安全编程?
答案:
TypeScript 的类型系统帮助预防常见安全问题。
案例:
typescript
// 输入验证
import { z } from 'zod'
const UserSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
age: z.number().min(0).max(150)
})
type UserInput = z.infer<typeof UserSchema>
function createUser(input: unknown): UserInput {
return UserSchema.parse(input) // 运行时验证
}
// 防止 SQL 注入
type SafeQuery = string & { __brand: 'SafeQuery' }
function escapeString(str: string): SafeQuery {
// 转义特殊字符
const escaped = str.replace(/[\\'"]/g, '\\$&')
return escaped as SafeQuery
}
function query(sql: SafeQuery): void {
// 执行查询
}
// 防止 XSS
function escapeHtml(unsafe: string): string {
return unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
// 类型安全的权限检查
type Permission = 'read' | 'write' | 'delete'
type Role = 'admin' | 'editor' | 'viewer'
const rolePermissions: Record<Role, Permission[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read']
}
function hasPermission(role: Role, permission: Permission): boolean {
return rolePermissions[role].includes(permission)
}
// 敏感数据类型
interface HashedPassword {
algorithm: 'bcrypt' | 'argon2'
hash: string
salt: string
}
// 确保密码不会被明文存储
function hashPassword(password: string): HashedPassword {
// 哈希实现
}100. TypeScript 未来发展趋势?
答案:
TypeScript 持续发展,引入更多类型系统特性。
案例:
typescript
// 即将推出的特性
// 1. 类型修饰符中的 using 声明(资源管理)
{
using file = await openFile('data.txt')
// file 在作用域结束时自动关闭
}
// 2. 更好的元组类型推断
const tuple = [1, 'hello', true] as const
// 自动推断为 readonly [1, "hello", true]
// 3. 类型导入导出改进
import type { User } from './types'
export type { User }
// 4. 装饰器元数据(Stage 3)
function Log() {
return function (target: any, propertyKey: string) {
// 使用元数据 API
Reflect.defineMetadata('log', true, target, propertyKey)
}
}
// 5. 更好的错误信息
// TypeScript 5.0+ 提供更清晰的错误提示
// 6. 性能优化
// - 更快的类型检查
// - 更小的包体积
// - 更好的内存使用
// 7. 与 JavaScript 更好的互操作性
// - JSDoc 类型增强
// - 允许 .js 文件中的类型注释
// 8. 类型谓词改进
function isDefined<T>(value: T | undefined | null): value is T {
return value !== undefined && value !== null
}
// 9. 模板字面量类型增强
type EventName<T extends string> = `on${Capitalize<T>}`
type Events = EventName<'click' | 'hover'> // "onClick" | "onHover"
// 10. 类型收窄改进
function process(value: string | number) {
if (typeof value === 'string') {
// value 自动收窄为 string
value.toUpperCase()
}
}总结
本文档涵盖了 TypeScript 面试中最常问的 100 道题目,按重要性排序:
重要性分布
- 核心基础(1-10题):TypeScript 基础概念、类型系统、编译过程
- 类型系统进阶(11-25题):高级类型、类型推断、类型保护
- 泛型深入(26-40题):泛型约束、条件类型、映射类型
- 类与接口(41-55题):面向对象、访问修饰符、抽象类
- 类型操作与工具类型(56-70题):内置工具类型、自定义工具类型
- 工程化与配置(71-85题):tsconfig、构建工具、CI/CD
- 实践应用与设计原则(86-100题):框架集成、设计模式、最佳实践
学习建议
- 从基础开始:确保理解基本类型和类型推断
- 多写多练:通过实际项目加深理解
- 阅读源码:学习优秀开源项目的类型定义
- 关注更新:TypeScript 持续演进,保持学习
面试技巧
- 理解原理:不仅要知道怎么用,还要知道为什么
- 结合实际:用项目经验支撑你的答案
- 展示深度:能够讲解类型系统的底层原理
- 主动扩展:回答时适当展示相关知识
文档完成时间:2026年
适用于 TypeScript 5.0+ 版本