Skip to content

TypeScript 经典面试题 100 道

按重要程度由高到低排序,每道题均包含详细答案和可运行案例

第1-10题:核心基础(最重要)

1. TypeScript 是什么?与 JavaScript 的区别?

答案:

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

核心区别:

特性JavaScriptTypeScript
类型系统动态类型静态类型
编译解释执行需要编译为 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 的基本类型包括:stringnumberbooleannullundefinedsymbolbigintanyunknownnevervoidobject

案例:

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. anyunknownnevervoid 之间的对比?

答案:

类型描述特点安全性
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)的区别?

答案:

特性InterfaceType 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> = handleEvent

17. 什么是结构化类型(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 的重要编译选项,使 nullundefined 成为独立的类型,需要显式处理。

案例:

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 表示永不存在的值的类型,用于:

  1. 函数永不返回(抛出异常或无限循环)
  2. 穷尽性检查
  3. 从联合类型中排除

案例:

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; // ❌ 没有任何类型可以赋值给 never

23. voidundefined 的区别?

答案:

  • 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 可以赋值给 void

24. 什么是 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([]) // undefined

27. 泛型默认值

答案:

泛型可以指定默认值,当无法推断类型时使用默认值。

案例:

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'> // string

29. 递归类型(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 }); // ❌ 缺少 y

31. 什么是 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 支持三种访问修饰符:publicprivateprotected,控制类成员的可见性。

案例:

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"); // ❌ 错误:必须是 number

53. 类如何实现索引访问类型?

答案:

类可以实现索引访问类型,通过定义索引签名来支持动态属性访问。

案例:

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 和 set

54. 什么是类的私有字段(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> // string

62. 如何实现 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>
  : T

64. 如何实现 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.json
json
// 根 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@v3

GitLab 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 = 42

91. 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.24s
typescript
// 运行时性能监控
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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}

// 类型安全的权限检查
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. 核心基础(1-10题):TypeScript 基础概念、类型系统、编译过程
  2. 类型系统进阶(11-25题):高级类型、类型推断、类型保护
  3. 泛型深入(26-40题):泛型约束、条件类型、映射类型
  4. 类与接口(41-55题):面向对象、访问修饰符、抽象类
  5. 类型操作与工具类型(56-70题):内置工具类型、自定义工具类型
  6. 工程化与配置(71-85题):tsconfig、构建工具、CI/CD
  7. 实践应用与设计原则(86-100题):框架集成、设计模式、最佳实践

学习建议

  1. 从基础开始:确保理解基本类型和类型推断
  2. 多写多练:通过实际项目加深理解
  3. 阅读源码:学习优秀开源项目的类型定义
  4. 关注更新:TypeScript 持续演进,保持学习

面试技巧

  1. 理解原理:不仅要知道怎么用,还要知道为什么
  2. 结合实际:用项目经验支撑你的答案
  3. 展示深度:能够讲解类型系统的底层原理
  4. 主动扩展:回答时适当展示相关知识

文档完成时间:2026年

适用于 TypeScript 5.0+ 版本

基于 VitePress 的本地知识库