← 返回首页← 专题总览

TypeScript 类型进阶与面试题

- [第一部分:TypeScript 类型系统深入](#第一部分typescript-类型系统深入)
前端架构师通关手册 · 专题 22

目录


第一部分:TypeScript 类型系统深入

1.1 高级类型操作

联合类型(Union Types)与交叉类型(Intersection Types)

// 联合类型:值可以是多种类型之一
type StringOrNumber = string | number;

// 交叉类型:合并多个类型为一种
interface Name {
  name: string;
}
interface Age {
  age: number;
}

type Person = Name & Age;
// 等价于 { name: string; age: number; }

// 实际应用:混入(Mixin)模式
function extend<T, U>(first: T, second: U): T & U {
  return { ...first, ...second };
}

const john = extend({ name: "John" }, { age: 30 });
// john: { name: string } & { age: number }

// 联合类型的类型守卫
function processValue(value: StringOrNumber) {
  if (typeof value === "string") {
    // TypeScript 知道这里是 string
    return value.toUpperCase();
  } else {
    // TypeScript 知道这里是 number
    return value.toFixed(2);
  }
}

// 可辨识联合(Discriminated Unions)
interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: 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.sideLength ** 2;
    case "triangle":
      return (shape.base * shape.height) / 2;
    default:
      // 穷尽性检查
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}

keyof 与 typeof 高级用法

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

// keyof 获取对象所有键的联合类型
type UserKeys = keyof User; // "id" | "name" | "email" | "createdAt"

// 获取对象值的类型
type UserValues = User[keyof User]; // number | string | Date

// 泛型约束中使用 keyof
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user: User = { id: 1, name: "Alice", email: "alice@test.com", createdAt: new Date() };
const name = getProperty(user, "name"); // string
const id = getProperty(user, "id");     // number

// typeof 获取表达式的类型
const config = {
  host: "localhost",
    port: 3000,
  debug: true,
} as const;

type Config = typeof config;
// { readonly host: "localhost"; readonly port: 3000; readonly debug: true; }

// 索引访问类型
type HostType = Config["host"]; // "string literal type"
type PortType = Config["port"]; // "number literal type"

类型映射的高级模式

// 将对象所有属性变为可选
interface User {
  id: number;
  name: string;
  age: number;
}

// 手动 Partial 实现
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

// 将对象所有属性变为只读
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

// 使用 as 重映射键(TS 4.1+)
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// {
//   getId: () => number;
//   getName: () => string;
//   getAge: () => number;
// }

// 过滤特定类型的属性
type PickByType<T, ValueType> = {
  [K in keyof T as T[K] extends ValueType ? K : never]: T[K];
};

type StringProps = PickByType<User, string>; // { name: string }
type NumberProps = PickByType<User, number>; // { id: number; age: number }

1.2 条件类型与 infer

条件类型深入

// 条件类型的分布式特性
type IsString<T> = T extends string ? "yes" : "no";

type Result1 = IsString<string>;            // "yes"
type Result2 = IsString<number>;            // "no"
type Result3 = IsString<string | number>;   // "yes" | "no" (分布式)

// 禁用分布:用元组包裹
type IsStringNoDist<T> = [T] extends [string] ? "yes" : "no";

type Result4 = IsStringNoDist<string | number>; // "no"

// 实际应用:类型过滤
type ExtractStrings<T> = T extends string ? T : never;
type Filtered1 = ExtractStrings<string | number | boolean>; // string

// 嵌套条件类型
type TypeName<T> =
  T extends string ? "string" :
  T extends number ? "number" :
  T extends boolean ? "boolean" :
  T extends undefined ? "undefined" :
  T extends Function ? "function" :
  "object";

type T1 = TypeName<"hello">;      // "string"
type T2 = TypeName<42>;           // "number"
type T3 = TypeName<true>;         // "boolean"
type T4 = TypeName<() => void>;   // "function"

infer 关键字详解

// 提取数组元素类型
type Flatten<T> = T extends Array<infer U> ? U : T;

type T1 = Flatten<string[]>;       // string
type T2 = Flatten<number[][]>;     // number[]
type T3 = Flatten<string>;         // string

// 提取函数返回值类型(内置 ReturnType)
type MyReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : never;

function createUser() {
  return { id: 1, name: "Alice", email: "alice@test.com" };
}

type UserFromFn = MyReturnType<typeof createUser>;
// { id: number; name: string; email: string }

// 提取函数参数类型(内置 Parameters)
type MyParameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;

type Params = MyParameters<typeof createUser>; // []

function updateUser(id: number, data: { name: string }) {}
type UpdateParams = MyParameters<typeof updateUser>;
// [number, { name: string }]

// 提取 Promise 解包类型(内置 Awaited)
type MyAwaited<T> =
  T extends null | undefined ? T :
  T extends object & { then(onfulfilled: infer F): any } ?
    F extends ((value: infer V, ...args: any) => any) ?
      MyAwaited<V> :
      never :
  T;

type P1 = MyAwaited<Promise<string>>;              // string
type P2 = MyAwaited<Promise<Promise<number>>>;     // number
type P3 = MyAwaited<Promise<string> | string>;     // string

// infer 多个位置:提取构造函数参数
type MyInstanceType<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: any) => infer R ? R : any;

class UserClass {
  id!: number;
  name!: string;
}

type UserInstance = MyInstanceType<typeof UserClass>;
// UserClass { id: number; name: string; }

复杂 infer 模式

// 提取对象属性名中匹配特定模式的键
type EventConfig = {
  onClick: () => void;
  onHover: () => void;
  onSubmit: () => void;
  title: string;
  className: string;
};

type ExtractEventKeys<T> = {
  [K in keyof T]: K extends `on${infer Event}` ? Event : never;
}[keyof T];

type Events = ExtractEventKeys<EventConfig>;
// "Click" | "Hover" | "Submit"

// 提取 URL 参数
type ExtractRouteParams<T extends string> =
  T extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractRouteParams<Rest>
    : T extends `${string}:${infer Param}`
      ? Param
      : never;

type Params1 = ExtractRouteParams<"/user/:id">;           // "id"
type Params2 = ExtractRouteParams<"/user/:id/post/:postId">; // "id" | "postId"

// 函数柯里化类型推断
type Curried<P extends any[], R> =
  <T extends any[]>(...args: T) =>
    T extends [] ? Curried<P, R> :
    T extends any[] ?
      T["length"] extends P["length"] ? R :
      T extends [infer ...infer Rest] ?
        Rest["length"] extends 0 ? R :
        Curried<P, R> :
      never :
    never;

// 字符串模板字面量类型 -- 模板匹配
type ReplaceAll<
  Str extends string,
  From extends string,
  To extends string
> = From extends "" ? Str :
  Str extends `${infer Prefix}${From}${infer Suffix}` ?
    `${Prefix}${To}${ReplaceAll<Suffix, From, To>}` :
    Str;

type Replaced = ReplaceAll<"hello world, hello ts", "hello", "hi">;
// "hi world, hi ts"

1.3 映射类型与模板字面量类型

映射类型进阶

// 深度 Partial
type DeepPartial<T> = T extends object
  ? { [P in keyof T]?: DeepPartial<T<P>> }
  : T;

interface Company {
  name: string;
  address: {
    city: string;
    zip: string;
  };
}

type PartialCompany = DeepPartial<Company>;
// 所有层级都是可选的

// 深度 Required
type DeepRequired<T> = T extends object
  ? { [P in keyof T]-?: DeepRequired<T[P]> }
  : T;

// 选择性深可选
type DeepPartialExcept<T, K extends keyof T> = {
  [P in K]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
} & {
  [P in Exclude<keyof T, K>]: T[P];
};

// 将方法类型改为异步版本
type AsyncMethods<T> = {
  [K in keyof T]: T[K] extends (...args: infer A) => infer R
    ? (...args: A) => Promise<R>
    : T[K];
};

interface ApiService {
  getUsers(): User[];
  createUser(name: string): User;
}

type AsyncApiService = AsyncMethods<ApiService>;
// {
//   getUsers(): Promise<User[]>;
//   createUser(name: string): Promise<User>;
// }

// 将属性名添加前缀
type PrefixKeys<T, Prefix extends string> = {
  [K in keyof T as `${Prefix}${Capitalize<string & K>}`]: T[K];
};

type PrefixedUser = PrefixKeys<User, "user">;
// { userId: number; userName: string; userEmail: string; userCreatedAt: Date }

模板字面量类型

// 事件名生成
type ElementEventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = ElementEventName<"click">;   // "onClick"
type ChangeEvent = ElementEventName<"change">; // "onChange"

// CSS 属性名称
type CSSProperty = {
  color: string;
  fontSize: number;
  marginBottom: string;
};

type CSSPropertyKeys<T> = {
  [K in keyof T]: `--${KebabCase<K & string>}`;
}[keyof T];

// kebab-case 转换
type KebabCase<S extends string> =
  S extends `${infer C}${infer R}`
    ? C extends Lowercase<C>
      ? `${Lowercase<C>}${KebabCase<R>}`
      : `-${Lowercase<C>}${KebabCase<R>}`
    : "";

type CSSVars = CSSPropertyKeys<CSSProperty>;
// "--color" | "--font-size" | "--margin-bottom"

// HTTP 方法类型收窄
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";

type Endpoint<T extends string> = `/api/${T}`;

type UserEndpoints = Endpoint<"users" | "user">; // "/api/users" | "/api/user"

// 版本化 API
type VersionedEndpoint<
  Version extends "v1" | "v2" | "v3",
  Path extends string
> = `/api/${Version}/${Path}`;

type V1Endpoints = VersionedEndpoint<"v1", "users" | "orders" | "products">;
// "/api/v1/users" | "/api/v1/orders" | "/api/v1/products"

1.4 内置工具类型详解

每个工具类型的底层实现

// 1. Partial<T> -- 所有属性可选
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

// 2. Required<T> -- 所有属性必填
type MyRequired<T> = {
  [P in keyof T]-?: T[P]; // -? 移除可选修饰符
};

// 3. Readonly<T> -- 所有属性只读
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

// 4. Pick<T, K> -- 选取部分属性
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

// 5. Omit<T, K> -- 排除部分属性
type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

// 6. Record<K, T> -- 构建键值类型
type MyRecord<K extends keyof any, T> = {
  [P in K]: T;
};

// 7. Extract<T, U> -- 提取可赋值给 U 的类型
type MyExtract<T, U> = T extends U ? T : never;

// 8. Exclude<T, U> -- 排除可赋值给 U 的类型
type MyExclude<T, U> = T extends U ? never : T;

// 9. NonNullable<T> -- 排除 null 和 undefined
type MyNonNullable<T> = T extends null | undefined ? never : T;

// 10. ReturnType<T> -- 函数返回值类型
type MyReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : never;

// 11. Parameters<T> -- 函数参数类型(元组)
type MyParameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;

// 12. InstanceType<T> -- 构造函数实例类型
type MyInstanceType<T extends abstract new (...args: any) => any> =
  T extends abstract new (...args: any) => infer R ? R : any;

// 13. ThisParameterType<T> -- 函数 this 类型
type MyThisParameterType<T> =
  T extends (this: infer U, ...args: any[]) => any ? U : unknown;

// 14. OmitThisParameter<T> -- 移除 this 参数
type MyOmitThisParameter<T> =
  unknown extends ThisParameterType<T> ? T :
  T extends (...args: infer A) => infer R ? (...args: A) => R : T;

实用自定义工具类型

// 1. 选取指定类型的属性
type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface MixedUser {
  id: number;
  name: string;
  age: number;
  isActive: boolean;
}

type StringProps = PickByType<MixedUser, string>; // { name: string }

// 2. 深度Partial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 3. 不可变深度Readonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// 4. 将联合类型转为元组
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

type TupleOf<T> = UnionToIntersection<
  T extends any ? () => T : never
> extends () => infer R
  ? [...TupleOf<Exclude<T, R>>, R]
  : [];

// 5. 字面量类型转为宽泛类型
type WidenLiteral<T> =
  T extends string ? string :
  T extends number ? number :
  T extends boolean ? boolean :
  T;

// 6. Promise 的递归解包与重包装
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;

// 7. 获取对象可选键
type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

type UserOptional = OptionalKeys<User>; // "email" 如果是可选属性

// 8. 获取对象必填键
type RequiredKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

// 9. 函数统一返回类型(Promise)
type Promisify<T> = {
  [K in keyof T]: T[K] extends (...args: infer A) => infer R
    ? (...args: A) => Promise<R>
    : T[K];
};

// 10. UnionToTuple(高级版)
type UnionToTuple<T, U = T> = [T] extends [never] ? [] :
  T extends any ? [T, ...UnionToTuple<Exclude<U, T>>] : never;
// 注意:联合类型顺序可能不固定

// 11. 比较类型是否相同
type IsEqual<A, B> = [A] extends [B]
  ? [B] extends [A]
    ? true
    : false
  : false;

type Test1 = IsEqual<string, string>; // true
type Test2 = IsEqual<string, number>; // false
type Test3 = IsEqual<string | number, number | string>; // true (联合无序)

// 12. 合并两个接口(后者覆盖前者)
type Override<T1, T2> = Omit<T1, keyof T2> & T2;

interface BaseUser {
  id: number;
  name: string;
  role: string;
}

interface AdminUser extends Override<BaseUser, { role: "admin"; permissions: string[] }> {}
// { id: number; name: string; role: "admin"; permissions: string[] }

第二部分:TypeScript 装饰器

2.1 装饰器基础

注意:TypeScript 5.0+ 使用 ECMAScript Stage 3 装饰器标准。以下两种标准都会涉及。

装饰器启用配置

// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,    // TS 旧版装饰器(Babel 风格)
    "emitDecoratorMetadata": true,     // 旧版元数据反射
    "target": "ES2022"                 // 新版装饰器(Stage 3)不需要 experimentalDecorators
  }
}

装饰器基本概念

装饰器是一种特殊的声明,可以附加到类、方法、访问器、属性或参数上,以修改它们的行为。装饰器本质上是一个函数。

// 最简单的装饰器
function SimpleDecorator(target: any) {
  console.log("装饰的 target:", target);
}

@SimpleDecorator
class MyClass {}
// 输出: 装饰的 target: class MyClass { ... }

装饰器类型总览

装饰器类型用途签名参数
类装饰器修改/替换类定义(target: Function) => Function \</td><td>void
方法装饰器修改/替换方法(target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor \</td><td>void
属性装饰器初始化属性逻辑(target: any, propertyKey: string) => void
访问器装饰器修改 getter/setter(target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor \</td><td>void
参数装饰器标记参数信息(target: any, propertyKey: string, parameterIndex: number) => void

2.2 类装饰器

基本类装饰器

// 类装饰器:接收构造函数作为参数
function LogClass<T extends new (...args: any[]) => any>(constructor: T) {
  console.log(`类 ${constructor.name} 被创建`);

  // 可以返回新的构造函数来替换原类
  return class extends constructor {
    createdAt = new Date();

    constructor(...args: any[]) {
      super(...args);
      console.log(`${constructor.name} 实例化于 ${this.createdAt}`);
    }
  };
}

@LogClass
class Animal {
  constructor(public name: string) {}
}

const cat = new Animal("Cat");
// 输出:
// 类 Animal 被创建
// Animal 实例化于 2026-10-26T...
// cat.createdAt 存在

给类添加混入功能

// 混入(Mixin)装饰器
function Timestamped<T extends new (...args: any[]) => any>(Base: T) {
  return class extends Base {
    timestamp = Date.now();

    getTimestamp() {
      return new Date(this.timestamp);
    }
  };
}

function Loggable<T extends new (...args: any[]) => any>(Base: T) {
  return class extends Base {
    log(message: string) {
      console.log(`[${this.constructor.name}] ${message}`);
    }
  };
}

function Activatable<T extends new (...args: any[]) => any>(Base: T) {
  return class extends Base {
    isActive = false;

    activate() {
      this.isActive = true;
    }

    deactivate() {
      this.isActive = false;
    }
  };
}

// 装饰器从下到上执行,但类是从内向外包装
@Loggable
@Timestamped
@Activatable
class User {
  constructor(public name: string) {}
}

const user = new User("Alice");
user.activate();   // ✅ 来自 Activatable mixin
user.log("hello"); // ✅ 来自 Loggable mixin

单例模式装饰器

function Singleton<T extends new (...args: any[]) => any>(constructor: T) {
  let instance: T | null = null;

  const newConstructor: any = function (...args: any[]) {
    if (!instance) {
      instance = new constructor(...args);
    }
    return instance;
  };

  newConstructor.prototype = constructor.prototype;
  return newConstructor;
}

@Singleton
class DatabaseConnection {
  public connectionId: string;

  constructor() {
    this.connectionId = Math.random().toString(36).substring(7);
    console.log("创建新的数据库连接:", this.connectionId);
  }
}

const db1 = new DatabaseConnection();
const db2 = new DatabaseConnection();
console.log(db1 === db2); // true
console.log(db1.connectionId === db2.connectionId); // true

类型安全的类装饰器

// 带有明确类型约束的类装饰器
function Component<T extends new (...args: any[]) => {}>(
  config: { tag: string }
) {
  return function (target: T): T & { tag: string } {
    (target as any).tag = config.tag;
    return target as T & { tag: string };
  };
}

@Component({ tag: "my-button" })
class ButtonComponent {
  render() {
    return `<button></button>`;
  }
}

console.log((ButtonComponent as any).tag); // "my-button"

2.3 方法装饰器

方法计时装饰器

function MeasureTime(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();

    console.log(`${propertyKey} 执行耗时: ${(end - start).toFixed(2)}ms`);
    return result;
  };

  return descriptor;
}

class Calculator {
  @MeasureTime
  fibonacci(n: number): number {
    return n <= 1 ? n : this.fibonacci(n - 1) + this.fibonacci(n - 2);
  }
}

const calc = new Calculator();
calc.fibonacci(40);
// fibonacci 执行耗时: 1234.56ms

防抖/节流装饰器

// 防抖装饰器
function Debounce(delay: number) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    let timer: ReturnType<typeof setTimeout> | null = null;

    descriptor.value = function (...args: any[]) {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        originalMethod.apply(this, args);
        timer = null;
      }, delay);
    };

    return descriptor;
  };
}

// 节流装饰器
function Throttle(delay: number) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    let lastExecution = 0;

    descriptor.value = function (...args: any[]) {
      const now = Date.now();
      if (now - lastExecution >= delay) {
        lastExecution = now;
        originalMethod.apply(this, args);
      }
    };

    return descriptor;
  };
}

class SearchService {
  @Debounce(300)
  search(query: string) {
    console.log("搜索:", query);
    // API 调用...
  }

  @Throttle(1000)
  autoSave(data: any) {
    console.log("自动保存:", data);
    // 保存到服务器...
  }
}

缓存装饰器(带类型安全)

function Memoize() {
  return function <T extends (...args: any[]) => any>(
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    const cache = new Map<string, any>();

    descriptor.value = function (...args: any[]): ReturnType<T> {
      const key = JSON.stringify(args);

      if (cache.has(key)) {
        console.log(`[缓存命中] ${propertyKey}`);
        return cache.get(key);
      }

      const result = originalMethod.apply(this, args);
      cache.set(key, result);
      console.log(`[缓存未命中] ${propertyKey}`);
      return result;
    };

    return descriptor;
  };
}

class MathService {
  @Memoize()
  expensiveCalculation(n: number): number {
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
      result += Math.sqrt(i + n);
    }
    return result;
  }
}

const service = new MathService();
service.expensiveCalculation(10); // [缓存未命中]
service.expensiveCalculation(10); // [缓存命中]
service.expensiveCalculation(20); // [缓存未命中]

权限验证装饰器

// 元数据存储
const METADATA_KEY = "method:permissions";

function RequirePermissions(...permissions: string[]) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    Reflect.defineMetadata(METADATA_KEY, permissions, target, propertyKey);

    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      const required: string[] = Reflect.getMetadata(METADATA_KEY, target, propertyKey);
      const userPermissions: string[] = (this as any).user?.permissions ?? [];

      const hasAll = required.every(p => userPermissions.includes(p));
      if (!hasAll) {
        throw new Error(`权限不足: 需要 ${required.join(", ")}`);
      }

      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

class AdminService {
  user = { name: "Admin", permissions: ["user:read", "user:write"] };

  @RequirePermissions("user:read")
  getUser(id: number) {
    return { id, name: "User" };
  }

  @RequirePermissions("user:write", "admin:delete")
  deleteUser(id: number) {
    console.log("删除用户:", id);
  }
}

2.4 属性装饰器

属性装饰器基础

// 属性装饰器接收目标对象和属性名
function LogAccess(target: any, propertyKey: string) {
  let value: any;

  const getter = function () {
    console.log(`获取 ${propertyKey}: ${value}`);
    return value;
  };

  const setter = function (newVal: any) {
    console.log(`设置 ${propertyKey}: ${newVal}`);
    value = newVal;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Product {
  @LogAccess
  price: number = 0;

  @LogAccess
  name: string = "";
}

const product = new Product();
product.name = "iPhone"; // 设置 name: iPhone
product.price = 9999;    // 设置 price: 9999
console.log(product.name); // 获取 name: iPhone

属性验证装饰器

function Validate(
  validator: (value: any) => boolean,
  errorMessage: string
) {
  return function (target: any, propertyKey: string) {
    let value: any;

    Object.defineProperty(target, propertyKey, {
      get() {
        return value;
      },
      set(newVal: any) {
        if (!validator(newVal)) {
          throw new Error(`[${propertyKey}] 验证失败: ${errorMessage}`);
        }
        value = newVal;
      },
      enumerable: true,
      configurable: true,
    });
  };
}

class UserProfile {
  @Validate(
    (v: string) => v.length >= 2 && v.length <= 50,
    "用户名长度需在 2-50 之间"
  )
  name: string = "";

  @Validate(
    (v: number) => v >= 0 && v <= 150,
    "年龄需在 0-150 之间"
  )
  age: number = 0;

  @Validate(
    (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
    "邮箱格式不正确"
  )
  email: string = "";
}

const profile = new UserProfile();
profile.name = "Alice";        // ✅
profile.age = 25;              // ✅
profile.email = "a@b.com";     // ✅

// profile.name = "A";          // ❌ 验证失败
// profile.age = -1;            // ❌ 验证失败
// profile.email = "invalid";   // ❌ 验证失败

2.5 参数装饰器

参数装饰器基础

function Required(
  target: any,
  propertyKey: string,
  parameterIndex: number
) {
  // 存储参数必需信息
  const existingRequired: number[] =
    Reflect.getOwnMetadata("validation:required", target, propertyKey) || [];
  existingRequired.push(parameterIndex);
  Reflect.defineMetadata(
    "validation:required",
    existingRequired,
    target,
    propertyKey
  );
}

// 可以与方法装饰器配合使用
function ValidateParams(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const requiredParams: number[] =
      Reflect.getOwnMetadata("validation:required", target, propertyKey) || [];

    for (const index of requiredParams) {
      if (args[index] === undefined || args[index] === null) {
        throw new Error(
          `${propertyKey} 的参数 ${index} 为必填项`
        );
      }
    }

    return originalMethod.apply(this, args);
  };

  return descriptor;
}

class UserService {
  @ValidateParams
  createUser(
    @Required name: string,
    @Required email: string,
    age?: number
  ) {
    return { name, email, age };
  }
}

const service = new UserService();
service.createUser("Alice", "alice@test.com"); // ✅
// service.createUser(); // ❌ 参数 0 为必填项

2.6 装饰器工厂与执行顺序

装饰器工厂

可以接受参数并返回实际装饰器函数的函数:

// 装饰器工厂
function Retry(maxRetries: number, backoffMs: number = 100) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      let retries = 0;

      while (retries < maxRetries) {
        try {
          return await originalMethod.apply(this, args);
        } catch (error) {
          retries++;
          if (retries >= maxRetries) throw error;

          const delay = backoffMs * Math.pow(2, retries);
          console.log(`重试第 ${retries} 次,等待 ${delay}ms`);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    };

    return descriptor;
  };
}

class ApiClient {
  @Retry(3, 200)
  async fetchData(url: string) {
    const response = await fetch(url);
    if (!response.ok) throw new Error("请求失败");
    return response.json();
  }
}

装饰器执行顺序

// 装饰器执行顺序规则:
// 1. 从上到下计算装饰器表达式
// 2. 从下到上(从右到左)应用装饰器
// 3. 参数装饰器 -> 方法装饰器 -> 访问器装饰器 -> 类装饰器
// 4. 同一位置的多个装饰器:从左到右求值(从外到内包裹)

function decoratorA(name: string) {
  console.log(`   [${name}] 工厂返回装饰器`);
  return function (target: any) {
    console.log(`   [${name}] 装饰器执行`);
  };
}

console.log("开始装饰器计算...");

@decoratorA("First")
@decoratorA("Second")
@decoratorA("Third")
class MyClass {}

// 输出:
// 开始装饰器计算...
//   [First] 工厂返回装饰器   ← 装饰器工厂从上到下求值
//   [Second] 工厂返回装饰器
//   [Third] 工厂返回装饰器
//   [Third] 装饰器执行       ← 装饰器从下到上(从右到左)执行
//   [Second] 装饰器执行
//   [First] 装饰器执行

第三部分:实际应用

3.1 实战:构建 Validation 装饰器系统

import "reflect-metadata";

// ============ 内置验证器 ============

const validateKey = "validator:rules";

interface ValidationRule<T> {
  validate: (value: T) => boolean;
  message: string;
}

function createRule<T>(
  validate: (value: T) => boolean,
  message: string
): ValidationRule<T> {
  return { validate, message };
}

// 内置规则
const Rules = {
  required: <T>(): ValidationRule<T> =>
    createRule<T>(v => v !== undefined && v !== null && v !== "", "字段不能为空"),

  minLength: (len: number): ValidationRule<string> =>
    createRule<string>(v => v.length >= len, `最小长度为 ${len}`),

  maxLength: (len: number): ValidationRule<string> =>
    createRule<string>(v => v.length <= len, `最大长度为 ${len}`),

  min: (minVal: number): ValidationRule<number> =>
    createRule<number>(v => v >= minVal, `最小值为 ${minVal}`),

  max: (maxVal: number): ValidationRule<number> =>
    createRule<number>(v => v <= maxVal, `最大值为 ${maxVal}`),

  pattern: (regex: RegExp): ValidationRule<string> =>
    createRule<string>(v => regex.test(v), `不匹配正则 ${regex}`),
};

// ============ 属性装饰器 ============

function Rule<T>(...rules: ValidationRule<T>[]) {
  return function (target: any, propertyKey: string) {
    const existing: Record<string, ValidationRule<any>[]> =
      Reflect.getOwnMetadata(validateKey, target) || {};

    existing[propertyKey] = rules;
    Reflect.defineMetadata(validateKey, existing, target);
  };
}

// ============ 验证逻辑 ============

export function validate(instance: any): Record<string, string[]> {
  const target = Object.getPrototypeOf(instance);
  const rules: Record<string, ValidationRule<any>[]> =
    Reflect.getOwnMetadata(validateKey, target) || {};

  const errors: Record<string, string[]> = {};

  for (const [key, fieldRules] of Object.entries(rules)) {
    const value = (instance as any)[key];
    const fieldErrors: string[] = [];

    for (const rule of fieldRules) {
      if (!rule.validate(value)) {
        fieldErrors.push(rule.message);
      }
    }

    if (fieldErrors.length > 0) {
      errors[key] = fieldErrors;
    }
  }

  return errors;
}

export function isValid(instance: any): boolean {
  return Object.keys(validate(instance)).length === 0;
}

// ============ 使用示例 ============

class RegistrationForm {
  @Rule(Rules.required(), Rules.minLength(2), Rules.maxLength(50))
  name: string = "";

  @Rule(Rules.required(), Rules.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/))
  email: string = "";

  @Rule(Rules.required(), Rules.min(18), Rules.max(120))
  age: number = 0;
}

const form = new RegistrationForm();
console.log(validate(form));
// { name: ["字段不能为空"], email: ["字段不能为空"], age: ["字段不能为空"] }

form.name = "Al";
form.email = "invalid";
form.age = 15;
console.log(validate(form));
// { name: ["最小长度为 2"], email: ["不匹配正则"], age: ["最小值为 18"] }

form.name = "Alice";
form.email = "alice@test.com";
form.age = 25;
console.log(isValid(form)); // true

3.2 实战:实现依赖注入

import "reflect-metadata";

// ============ 容器核心 ============

type Constructor<T = {}> = new (...args: any[]) => T;

const INJECT_KEY = "inject:types";
const CONTAINER_KEY = symbols ? Symbol.for("DI:Container") : "DI:Container";

class DIContainer {
  private instances = new Map<string, any>();
  private singletons = new Map<string, any>();

  register<T>(token: string, impl: Constructor<T>): void {
    this.instances.set(token, impl);
  }

  registerSingleton<T>(token: string, impl: Constructor<T>): void {
    this.singletons.set(token, impl);
  }

  resolve<T>(token: string): T {
    // 先检查单例
    if (this.singletons.has(token)) {
      if (!cache.has(token)) {
        const impl = this.singletons.get(token)!;
        const instance = this.createInstance<T>(impl);
        cache.set(token, instance);
      }
      return cache.get(token);
    }

    // 瞬态
    const impl = this.instances.get(token) || this.singletons.get(token);
    if (!impl) throw new Error(`未找到注册项: ${token}`);
    return this.createInstance<T>(impl);
  }

  private createInstance<T>(impl: Constructor<T>): T {
    // 获取构造函数参数类型
    const paramTypes: Constructor[] =
      Reflect.getMetadata("design:paramtypes", impl) || [];

    // 获取注入的 token 映射
    const injections: (string | undefined)[] =
      Reflect.getOwnMetadata(INJECT_KEY, impl) || [];

    const args = paramTypes.map((type, index) => {
      const token = injections[index] || type.name;
      return this.resolve(token);
    });

    return new impl(...args);
  }
}

const cache = new Map<string, any>();
export const container = new DIContainer();

// ============ 便捷装饰器 ============

export function Injectable(token?: string) {
  return function <T extends Constructor>(target: T) {
    container.register(token || token?.toString() || target.name, target);
  };
}

export function Inject(token: string) {
  return function (target: any, _key: string, index: number) {
    const existing: (string | undefined)[] =
      Reflect.getOwnMetadata(INJECT_KEY, target) || [];
    existing[index] = token;
    Reflect.defineMetadata(INJECT_KEY, existing, target);
  };
}

// ============ 使用示例 ============

interface ILogger {
  log(message: string): void;
}

@Injectable("ILogger")
class ConsoleLogger implements ILogger {
  log(message: string) {
    console.log(`[LOG] ${message}`);
  }
}

interface IRepository<T> {
  findAll(): T[];
}

@Injectable("IUserRepository")
class UserRepository implements IRepository<{ name: string }> {
  findAll() {
    return [{ name: "Alice" }, { name: "Bob" }];
  }
}

@Injectable("UserService")
class UserService {
  constructor(
    @Inject("ILogger") private logger: ILogger,
    @Inject("IUserRepository") private userRepo: IRepository<{ name: string }>
  ) {}

  getUsers() {
    this.logger.log("获取所有用户");
    return this.userRepo.findAll();
  }
}

// 使用
const userService = container.resolve<UserService>("UserService");
const users = userService.getUsers();
// [LOG] 获取所有用户
// [{ name: "Alice" }, { name: "Bob" }]

3.3 实战:Vue/React 中的类型体操

提取组件 Props 类型

// Vue 组件 Props 类型提取
type ExtractPropTypes<T> = T extends { props: infer P }
  ? P
  : never;

interface ButtonProps {
  type: "primary" | "secondary" | "danger";
  size: "small" | "medium" | "large";
  disabled?: boolean;
  onClick?: () => void;
}

type ButtonPropType = ExtractPropTypes<{ props: ButtonProps }>;
// { type: "primary" | "secondary" | "danger"; size: ...; disabled?: boolean; onClick?: ... }

// Vue 3 defineComponent 类型
type ComponentProps<T> = T extends (...args: any) => any
  ? Parameters<T>[0]
  : T extends { props: infer P }
    ? P
    : {};

// 从 Vue 组件中提取 emit 事件名
type ComponentEmits<T> = T extends { emits: infer E }
  ? E extends string[]
    ? E[number]
    : never
    : never;

// React 组件 Props 提取
type ExtractReactProps<T extends React.FC<any>> = React.ComponentProps<T>;

type ReactFunctionComponent<T extends (...args: any) => any> =
  Parameters<T>[0];

路由参数类型安全

// 从路由路径提取参数类型
interface RouteConfig<
  Path extends string,
  Params extends Record<string, unknown> = ExtractRouteParams<Path>
> {
  path: Path;
  component: React.FC<{ params: Params }>;
}

// 路由列表类型安全
type Routes = [
  RouteConfig<"/", {}>,
  RouteConfig<"/user/:id", { id: string }>,
  RouteConfig<"/user/:id/post/:postId", { id: string; postId: string }>
];

// API 客户端类型安全
type ApiMethod = "GET" | "POST" | "PUT" | "DELETE";

type EndpointDef<
  Method extends ApiMethod,
  Path extends string,
  ResponseBody,
  RequestBody = never
> = {
  method: Method;
  path: Path;
  response: ResponseBody;
  request: RequestBody;
};

// API 定义
interface ApiResponse<T> {
  data: T;
  status: number;
}

type User = { id: number; name: string };

type ApiRoutes = {
  "GET /users": EndpointDef<"GET", "/users", ApiResponse<User[]>>;
  "GET /user/:id": EndpointDef<"GET", "/user/:id", ApiResponse<User>>;
  "POST /user": EndpointDef<"POST", "/user", ApiResponse<User>, { name: string }>;
};

// 类型安全的 fetch 封装
type FetchApi<T extends Record<string, EndpointDef<any, any, any, any>>> = {
  [K in keyof T]: (
    ...args: T[K]["request"] extends never
      ? []
      : [body: T[K]["request"]]
  ) => Promise<T[K]["response"]>;
};

type Client = FetchApi<ApiRoutes>;
// {
//   "GET /users": () => Promise<ApiResponse<User[]>>;
//   "GET /user/:id": () => Promise<ApiResponse<User>>;
//   "POST /user": (args: [{ name: string }]) => Promise<ApiResponse<User>>;
// }

类型安全的 Redux / Pinia 状态管理

// 类型安全的 Action Creator
type ActionCreators<T extends Record<string, (...args: any[]) => any>> = {
  [K in keyof T]: (...args: Parameters<T[K]>) => {
    type: K;
    payload: TupleToPayload<Awaited<ReturnType<T[K]>>>;
  };
};

type TupleToPayload<T> = T extends undefined
  ? never
  : T extends [...infer Rest]
    ? Rest
    : [T];

// 使用示例
const createStore = <Actions extends Record<string, (...args: any[]) => any>>(
  actions: Actions,
  initialState: object
) => {
  type ActionList = ActionCreators<Actions>;
  type ActionType = {
    [K in keyof ActionList]: ReturnType<ActionList[K]>;
  }[keyof ActionList];

  return {
    actions: {} as ActionList,
    dispatch: (action: ActionType) => {},
  };
};

const store = createStore(
  {
    increment: (amount: number) => ({ amount }),
    reset: () => {},
    setName: (name: string) => ({ name }),
  },
  { count: 0, name: "" }
);

// store.actions.increment(5) -> { type: "increment", payload: [5] }
// store.actions.setName("Alice") -> { type: "setName", payload: ["Alice"] }

第四部分:经典面试题与详解

面试题 1:实现一个 DeepReadonly 类型

题目:请实现一个 DeepReadonly<T> 类型,使对象及其所有嵌套对象的属性都变为只读。
// 错误实现(常见陷阱)
type DeepReadonly_Wrong<T> = {
  readonly [P in keyof T]: T[P] extends object ? T[P] : T[P];
  // ❌ 对函数、数组、Date 等对象类型处理不正确
};

// ✅ 正确实现
type DeepReadonly<T> = T extends object
  ? T extends Function  // 函数不是对象类型处理
    ? T
    : { readonly [P in keyof T]: DeepReadonly<T[P]> }
  : T;

// 测试
interface NestedUser {
  name: string;
  address: {
    city: string;
    details: {
      street: string;
      zip: string;
    };
    coords: [number, number];
  };
  greet: () => void;
}

type ROUser = DeepReadonly<NestedUser>;
/*
{
  readonly name: string;
  readonly address: {
    readonly city: string;
    readonly details: {
      readonly street: string;
      readonly zip: string;
    };
    readonly coords: [number, number]; // ✅ 元组保持不变
  };
  readonly greet: () => void; // ✅ 函数保持不变
}
*/

const user: ROUser = {} as any;
// user.name = "new"; // ❌ 无法分配到 "name" ,因为它是只读属性
// user.address.city = "new"; // ❌ 无法分配到 "city" ,因为它是只读属性
考察点:
  1. 递
归类型的理解
  1. 函
数类型的判断(T extends Function)
  1. 映
射类型 + readonly修饰符

面试题 2:实现 ConditionalPick

题目:实现一个类型 ConditionalPick<T, Condition>,从类型 T 中选取所有值满足 Condition 的属性。
// ✅ 实现(使用 TS 4.1+ 的 Key Remapping)
type ConditionalPick<T, Condition> = {
  [K in keyof T as T[K] extends Condition ? K : never]: T[K];
};

// 测试
interface MixedData {
  id: number;
  name: string;
  age: number;
  isActive: boolean;
  tags: string[];
}

type StringOnly = ConditionalPick<MixedData, string>;
// { name: string }

type BooleanOnly = ConditionalPick<MixedData, boolean>;
// { isActive: boolean }

type NumberOrString = ConditionalPick<MixedData, number | string>;
// { id: number; name: string; age: number }

// 进阶:深度条件选取
type DeepConditionalPick<T, Condition> = {
  [K in keyof T as T[K] extends Condition | object ? K : never]:
    T[K] extends Condition
      ? T[K]
      : T[K] extends object
        ? DeepConditionalPick<T[K], Condition>
        : never;
};
考察点:
  1. K
ey Remapping(as 子句)
  1. 条
件类型的分布特性
  1. 递
归类型应用

面试题 3:将联合类型转换为交叉类型

题目:实现 UnionToIntersection<U>,把联合类型 A | B | C 转换为 A & B & C。
// ✅ 实现(利用条件类型的分布式特性 + 逆变位置推断)
type UnionToIntersection<U> =
  (U extends any ? (args: U) => void : never) extends
    (args: infer I) => void
      ? I
      : never;

// 原理分析:
// 1. (U extends any ? (args: U) => void : never)
//    分布式条件类型,将 U = A | B | C 展开为:
//    ((args: A) => void) | ((args: B) => void) | ((args: C) => void)
//
// 2. 在逆变位置(函数参数),infer I 会推断出交叉类型:
//    ((args: A) => void) | ((args: B) => void) | ((args: C) => void) extends (args: infer I) => void
//    -> I = A & B & C (函数参数逆变导致联合变交叉)

// 测试
type Test1 = UnionToIntersection<{ a: string } | { b: number }>;
// { a: string } & { b: number }

type Test2 = UnionToIntersection<
  { name: string } | { age: number } | { active: boolean }
>;
// { name: string } & { age: number } & { active: boolean }

// 实际应用:合并路由参数
type RouteParams = UnionToIntersection<
  | { userId: string }
  | { postId: string }
  | { commentId: string }
  | {}
>;
// 最终: { userId: string } & { postId: string } & { commentId: string }
考察点:
  1. 条
件类型的分布式特性
  1. 函
数参数的逆变(contravariance)
  1. <
code>infer 在逆变位置推断交叉类型

面试题 4:实现一个类型安全的 EventEmitter

题目:实现类型安全的事件发射器,要求事件名与回调参数类型匹配。
// ✅ 实现
type EventMap = Record<string, any>;

type EventCallback<T> = (payload: T) => void;

interface TypedEmitter<Events extends EventMap> {
  on<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>): void;
  off<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>): void;
  emit<K extends keyof Events>(event: K, payload: Events[K]): void;
}

// 使用
interface AppEvents {
  "user:login": { userId: string; name: string };
  "user:logout": { userId: string };
  "error": { code: number; message: string };
  "notification": string;
}

class EventEmitter<Events extends EventMap> implements TypedEmitter<Events> {
  private listeners = new Map<keyof Events, Set<EventCallback<any>>>();

  on<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    this.listeners.get(event)!.add(callback);
  }

  off<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>) {
    this.listeners.get(event)?.delete(callback);
  }

  emit<K extends keyof Events>(event: K, payload: Events[K]) {
    this.listeners.get(event)?.forEach(cb => cb(payload));
  }
}

// 使用示例
const emitter = new EventEmitter<AppEvents>();

// ✅ 类型安全
emitter.on("user:login", (payload) => {
  console.log(payload.userId);   // string ✅
  console.log(payload.name);     // string ✅
});

emitter.emit("user:login", { userId: "1", name: "Alice" }); // ✅
emitter.emit("error", { code: 404, message: "Not Found" }); // ✅

// ❌ 类型错误
// emitter.emit("user:login", { userId: 123 }); // ❌ name 缺失,userId 类型错误
// emitter.emit("unknown", {});                 // ❌ 事件名不存在
考察点:
  1. 泛
型约束与索引类型
  1. 映
射类型与 keyof 的配合
  1. 类
型安全的 API 设计

面试题 5:实现 DeepKeyOf 和 DeepValueOf

题目:实现获取对象所有嵌套键路径的类型,以及根据路径获取值类型。
// ✅ DeepKeyOf: 获取所有嵌套键的路径(用点号连接)
type DeepKeyOf<T, Prefix extends string = ""> = T extends object
  ? T extends any[] // 排除数组
    ? Prefix
    : {
        [K in keyof T & string]: Prefix extends ""
          ? K | DeepKeyOf<T[K], K>
          : `${Prefix}.${K}` | DeepKeyOf<T[K], `${Prefix}.${K}`>;
      }[keyof T & string]
  : Prefix;

// 测试
interface Company {
  name: string;
  address: {
    city: string;
    country: {
      code: string;
      name: string;
    };
  };
  employees: {
    name: string;
    role: string;
  }[];
}

type CompanyKeys = DeepKeyOf<Company>;
// "name"
// | "address"
// | "address.city"
// | "address.country"
// | "address.country.code"
// | "address.country.name"
// | "employees"

// ✅ DeepValueOf: 根据路径获取值类型
type DeepValueOf<T, Path extends string> =
  Path extends `${infer Key}.${infer Rest}`
    ? Key extends keyof T
      ? DeepValueOf<T[Key], Rest>
      : never
    : Path extends keyof T
      ? T[Path]
      : never;

// 测试
type CityType = DeepValueOf<Company, "address.city">;           // string
type CountryCodeType = DeepValueOf<Company, "address.country.code">; // string
type NameType = DeepValueOf<Company, "name">;                   // string

// ✅ 类型安全的 get 函数
function get<T, Path extends DeepKeyOf<T>>(
  obj: T,
  path: Path
): DeepValueOf<T, Path> {
  return path.split(".").reduce((acc: any, key) => acc?.[key], obj);
}

const company: Company = {
  name: "Acme",
  address: {
    city: "Beijing",
    country: { code: "CN", name: "China" },
  },
  employees: [],
};

const city = get(company, "address.city");       // string
const countryCode = get(company, "address.country.code"); // string
// const invalid = get(company, "address.invalid"); // ❌ 编译错误
考察点:
  1. 模
板字面量类型
  1. 递
归类型
  1. 条
件类型中的字符串推断
  1. 类
型安全的运行时函数

面试题 6:实现一个类型安全的 Builder 模式

题目:实现一个类型安全的 Builder,要求必填字段未设置时无法调用 build()。
// ✅ 实现
type MissingFields<T, Built> = keyof Omit<T, Built>;

class Builder<T, Built extends keyof T = never> {
  private data: Partial<T> = {};

  set<K extends keyof T & string>(
    key: K,
    value: T[K]
  ): Builder<T, Built | K> {
    this.data[key] = value;
    return this as any;
  }

  build(): T extends Pick<T, Built>
    ? T
    : { __error: `缺少必填字段: ${MissingFields<T, Built> & string}` } {
    return this.data as any;
  }
}

// 使用
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
}

const builder = new Builder<User>();

// 链式调用
const result = builder
  .set("id", 1)
  .set("name", "Alice")
  .set("email", "alice@test.com")
  .build();
// result 类型: User ✅

const builder2 = new Builder<User>();
const result2 = builder2
  .set("id", 1)
  .build();
// result2 类型: { __error: "缺少必填字段: name" | "缺少必填字段: email" | ... }

// 更优雅的实现:使用 Required 约束
type BuilderState<T, K extends keyof T> =
  Required<Pick<T, K>> & Partial<Omit<T, K>>;

class SafeBuilder<T> {
  constructor(private data: Partial<T> = {}) {}

  with<K extends keyof T>(key: K, value: K extends keyof T ? T[K] : never): SafeBuilder<T> {
    return new SafeBuilder<T>({ ...this.data, [key]: value } as Partial<T>);
  }

  build(this: SafeBuilder<T> & { data: Required<T> }): T {
    return this.data as T;
  }
}

// 使用
const user = new SafeBuilder<User>()
  .with("id", 1)
  .with("name", "Alice")
  .with("email", "alice@test.com")
  .build(); // ✅ 编译通过

// const badUser = new SafeBuilder<User>()
//   .with("id", 1)
//   .build(); // ❌ 编译错误:data 不满足 Required<T>
考察点:
  1. 泛
型状态追踪
  1. <
code>this 参数类型约束
  1. 条
件返回类型
  1. 类
型级别的"状态机"

面试题 7:装饰器实现一个完整的 HTTP 路由系统

题目:使用装饰器实现类似 NestJS 的路由系统。
import "reflect-metadata";

// ============ 元数据键 ============
const ROUTE_KEY = "route:metadata";
const CONTROLLER_KEY = "controller:prefix";
const INJECTABLE_KEY = "injectable";

// ============ 类型定义 ============
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";

interface RouteMetadata {
  method: HttpMethod;
  path: string;
  handler: string;
}

// ============ 装饰器 ============

function Controller(prefix: string = "") {
  return function <T extends new (...args: any[]) => any>(target: T) {
    Reflect.defineMetadata(CONTROLLER_KEY, prefix, target);
    return target;
  };
}

function createRouteDecorator(method: HttpMethod) {
  return function (path: string = "") {
    return function (
      target: any,
      propertyKey: string,
      _descriptor: PropertyDescriptor
    ) {
      const routes: RouteMetadata[] =
        Reflect.getOwnMetadata(ROUTE_KEY, target.constructor) || [];

      routes.push({ method, path, handler: propertyKey });
      Reflect.defineMetadata(ROUTE_KEY, routes, target.constructor);
    };
  };
}

const Get = createRouteDecorator("GET");
const Post = createRouteDecorator("POST");
const Put = createRouteDecorator("PUT");
const Delete = createRouteDecorator("Delete");

// ============ 路由注册器 ============

class Router {
  private routes: Array<{
    method: HttpMethod;
    path: string;
    handler: (req: any, res: any) => any;
  }[]> = [];

  register(controller: any) {
    const prefix: string = Reflect.getMetadata(CONTROLLER_KEY, controller) || "";
    const routes: RouteMetadata[] = Reflect.getMetadata(ROUTE_KEY, controller) || [];

    for (const route of routes) {
      const fullPath = prefix + route.path;
      const instance = new controller();
      const handler = instance[route.handler].bind(instance);

      this.routes.push([{
        method: route.method,
        path: fullPath,
        handler,
      }]);

      console.log(`[Router] ${route.method} ${fullPath} -> ${route.handler}`);
    }
  }
}

// ============ 使用示例 ============

@Controller("/api/users")
class UserController {
  @Get("/")
  findAll() {
    return [{ id: 1, name: "Alice" }];
  }

  @Get("/:id")
  findOne(id: number) {
    return { id, name: "Alice" };
  }

  @Post("/")
  create(data: any) {
    return { id: 2, ...data };
  }

  @Put("/:id")
  update(id: number, data: any) {
    return { id, ...data };
  }

  @Delete("/:id")
  remove(id: number) {
    return { deleted: id };
  }
}

// 注册路由
const router = new Router();
router.register(UserController);
// [Router] GET /api/users/ -> findAll
// [Router] GET /api/users/:id -> findOne
// [Router] POST /api/users/ -> create
// [Router] PUT /api/users/:id -> update
// [Router] DELETE /api/users/:id -> remove
考察点:
  1. 装
饰器工厂模式
  1. <
code>reflect-metadata 的使用
  1. 元
数据的设计模式
  1. 类
装饰器 + 方法装饰器配合

面试题 8:实现类型安全的 Immutable 更新

题目:实现一个 ImmutableUpdate<T, Path, Value> 类型,以及对应的运行时函数。
// ✅ 类型实现
type SetNestedValue<T, Path extends string, Value> =
  Path extends `${infer Key}.${infer Rest}`
    ? Key extends keyof T
      ? Omit<T, Key> & { [K in Key]: SetNestedValue<T[Key], Rest, Value> }
      : T
    : Path extends keyof T
      ? Omit<T, Path> & { [K in Path]: Value }
      : T;

// 测试
interface AppState {
  user: {
    profile: {
      name: string;
      age: number;
    };
    settings: {
      theme: "light" | "dark";
      notifications: boolean;
    };
  };
  posts: string[];
}

// 更新 user.profile.name
type UpdatedState = SetNestedValue<AppState, "user.profile.name", "Bob">;
/*
{
  user: {
    profile: {
      name: "Bob";  // ✅ 已更新
      age: number;
    };
    settings: { ... };
  };
  posts: string[];
}
*/

// ✅ 运行时实现
function setNested<T, P extends string, V>(
  obj: T,
  path: P,
  value: V
): SetNestedValue<T, P, V> {
  const keys = path.split(".");
  const result = { ...obj };

  let current: any = result;
  for (let i = 0; i < keys.length - 1; i++) {
    const key = keys[i];
    current[key] = { ...current[key] };
    current = current[key];
  }

  current[keys[keys.length - 1]] = value;
  return result as any;
}

// 使用
const state: AppState = {
  user: {
    profile: { name: "Alice", age: 25 },
    settings: { theme: "light", notifications: true },
  },
  posts: ["Hello"],
};

const newState = setNested(state, "user.profile.name", "Bob");
// newState.user.profile.name === "Bob"
// state.user.profile.name === "Alice" // 原对象不变
考察点:
  1. 模
板字面量类型拆分路径
  1. 递
归类型 + Omit 组合
  1. 不
可变数据更新模式
  1. 类
型与运行时的一致性

面试题 9:实现一个类型安全的 Store(类似 Vuex/Pinia)

题目:实现一个类型安全的状态管理 Store。
// ✅ 类型定义
type Getters<T> = {
  [K in keyof T]: (state: T) => any;
};

type Mutations<T> = {
  [K in keyof T]: (state: T, payload: any) => void;
};

type Actions<T> = {
  [K in keyof T]: (context: any, payload: any) => Promise<any> | void;
};

// 提取 Getter 返回类型
type GetterReturns<G> = {
  [K in keyof G]: G[K] extends (...args: any[]) => infer R ? R : never;
};

// 提取 Mutation Payload
type MutationPayloads<M> = {
  [K in keyof M]: M[K] extends (_: any, payload: infer P) => void ? P : never;
};

// 提取 Action Payload
type ActionPayloads<A> = {
  [K in keyof A]: A[K] extends (_: any, payload: infer P) => any ? P : never;
};

// ✅ Store 类型
interface StoreOptions<S, G, M, A> {
  state: S;
  getters?: G & ThisType<S & GetterReturns<G>>;
  mutations?: M & ThisType<S>;
  actions?: A & ThisType<S>;
}

interface Store<S, G, M, A> {
  state: S;
  getters: GetterReturns<G>;
  commit<K extends keyof M>(type: K, payload: MutationPayloads<M>[K]): void;
  dispatch<K extends keyof A>(
    type: K,
    payload: ActionPayloads<A>[K]
  ): Promise<any>;
}

// ✅ 创建 Store
function createStore<S, G, M, A>(
  options: StoreOptions<S, G, M, A>
): Store<S, G, M, A> {
  const state = reactive(options.state);
  const getters = {} as any;
  const mutations = options.mutations || {};
  const actions = options.actions || {};

  // 实现 getters
  if (options.getters) {
    for (const [key, fn] of Object.entries(options.getters)) {
      Object.defineProperty(getters, key, {
        get: () => fn.call(state),
      });
    }
  }

  return {
    state,
    getters,
    commit(type: string, payload: any) {
      mutations[type]?.(state, payload);
    },
    async dispatch(type: string, payload: any) {
      return actions[type]?.(state, payload);
    },
  } as any;
}

// 辅助函数(简化版)
function reactive<T extends object>(obj: T): T {
  return new Proxy(obj, {
    get(target, key) {
      return (target as any)[key];
    },
    set(target, key, value) {
      (target as any)[key] = value;
      return true;
    },
  });
}

// ✅ 使用示例
const store = createStore({
  state: {
    count: 0,
    user: null as { name: string } | null,
  },

  getters: {
    doubleCount: (state) => state.count * 2,
    isLoggedIn: (state) => state.user !== null,
  },

  mutations: {
    increment: (state, payload: { amount: number }) => {
      state.count += payload.amount;
    },
    setUser: (state, payload: { name: string }) => {
      state.user = payload;
    },
  },

  actions: {
    async fetchUser(_ctx, payload: { id: number }) {
      // 模拟 API 调用
      return { name: `User ${payload.id}` };
    },
  },
});

// 类型安全的使用
store.commit("increment", { amount: 5 });    // ✅
store.commit("setUser", { name: "Alice" });   // ✅
store.dispatch("fetchUser", { id: 1 });        // ✅

// store.commit("increment", { amount: "5" }); // ❌ 类型错误
// store.commit("unknown", {});                // ❌ 类型错误
// store.dispatch("fetchUser", { id: "1" });    // ❌ 类型错误

console.log(store.getters.doubleCount); // number
console.log(store.getters.isLoggedIn);  // boolean
考察点:
  1. 复
杂泛型约束
  1. <
code>ThisType 的使用
  1. 类
型提取(Payload 推断)
  1. 类
型安全的 API 设计

面试题 10:实现一个类型安全的 Schema 验证器

题目:实现一个类型安全的 Schema 定义和验证系统,Schema 定义即类型。
// ✅ Schema 类型定义
type SchemaType =
  | { type: "string"; minLength?: number; maxLength?: number; pattern?: RegExp }
  | { type: "number"; min?: number; max?: number }
  | { type: "boolean" }
  | { type: "array"; items: SchemaType }
  | { type: "object"; properties: Record<string, SchemaType> }
  | { type: "optional"; inner: SchemaType }
  | { type: "enum"; values: readonly string[] };

// ✅ Schema 到类型的映射
type SchemaToType<S extends SchemaType> =
  S extends { type: "string" } ? string :
  S extends { type: "number" } ? number :
  S extends { type: "boolean" } ? boolean :
  S extends { type: "array"; items: infer I extends SchemaType }
    ? SchemaToType<I>[] :
  S extends { type: "object"; properties: infer P extends Record<string, SchemaType> }
    ? { [K in keyof P]: SchemaToType<P[K]> } :
  S extends { type: "optional"; inner: infer I extends SchemaType }
    ? SchemaToType<I> | undefined :
  S extends { type: "enum"; values: readonly (infer V)[] }
    ? V :
  never;

// ✅ 简化 Schema 定义的辅助函数
const S = {
  string: (opts?: { minLength?: number; maxLength?: number }) =>
    ({ type: "string" as const, ...opts }),
  number: (opts?: { min?: number; max?: number }) =>
    ({ type: "number" as const, ...opts }),
  boolean: () => ({ type: "boolean" as const }),
  array: <T extends SchemaType>(items: T) =>
    ({ type: "array" as const, items }),
  object: <T extends Record<string, SchemaType>>(properties: T) =>
    ({ type: "object" as const, properties }),
  optional: <T extends SchemaType>(inner: T) =>
    ({ type: "optional" as const, inner }),
  enum: <const T extends readonly string[]>(values: T) =>
    ({ type: "enum" as const, values }),
};

// ✅ 使用
const UserSchema = S.object({
  name: S.string({ minLength: 2 }),
  age: S.number({ min: 0, max: 150 }),
  email: S.optional(S.string()),
  role: S.enum(["admin", "user", "guest"]),
  tags: S.array(S.string()),
});

type User = SchemaToType<typeof UserSchema>;
/*
type User = {
  name: string;
  age: number;
  email?: string;       // ✅ optional
  role: "admin" | "user" | "guest";  // ✅ enum
  tags: string[];        // ✅ array
}
*/

// ✅ 验证函数
function validate<S extends SchemaType>(
  value: unknown,
  schema: S
): { success: true; data: SchemaToType<S> } | { success: false; errors: string[] } {
  const errors: string[] = [];

  function check(val: unknown, s: SchemaType, path: string): boolean {
    switch (s.type) {
      case "string":
        if (typeof val !== "string") {
          errors.push(`${path}: 期望 string,得到 ${typeof val}`);
          return false;
        }
        if (s.minLength && val.length < s.minLength) {
          errors.push(`${path}: 最小长度为 ${s.minLength}`);
          return false;
        }
        return true;

      case "number":
        if (typeof val !== "number") {
          errors.push(`${path}: 期望 number,得到 ${typeof val}`);
          return false;
        }
        if (s.min !== undefined && val < s.min) {
          errors.push(`${path}: 最小值为 ${s.min}`);
          return false;
        }
        return true;

      case "boolean":
        if (typeof val !== "boolean") {
          errors.push(`${path}: 期望 boolean,得到 ${typeof val}`);
          return false;
        }
        return true;

      case "optional":
        if (val === undefined) return true;
        return check(val, s.inner, path);

      case "enum":
        if (!s.values.includes(val as string)) {
          errors.push(`${path}: 期望 ${s.values.join(" | ")},得到 ${val}`);
          return false;
        }
        return true;

      case "array":
        if (!Array.isArray(val)) {
          errors.push(`${path}: 期望 array,得到 ${typeof val}`);
          return false;
        }
        return (val as unknown[]).every((item, i) =>
          check(item, s.items, `${path}[${i}]`)
        );

      case "object":
        if (typeof val !== "object" || val === null) {
          errors.push(`${path}: 期望 object`);
          return false;
        }
        return Object.entries(s.properties).every(([key, propSchema]) =>
          check((val as any)[key], propSchema, `${path}.${key}`)
        );

      default:
        return false;
    }
  }

  const success = check(value, schema, "root");
  return success
    ? { success: true, data: value as SchemaToType<S> }
    : { success: false, errors };
}

// ✅ 使用验证
const result = validate(
  { name: "Alice", age: 25, role: "admin", tags: ["dev"] },
  UserSchema
);

if (result.success) {
  const user: User = result.data; // ✅ 类型安全
  console.log(user.name, user.role);
} else {
  console.log(result.errors);
}
考察点:
  1. 条
件类型的链式判断
  1. 递
归类型处理嵌套结构
  1. <
code>as const 与字面量类型收窄
  1. 类
型与运行时验证的一致性

面试题 11:实现一个类型安全的 EventBus 通配符系统

题目:实现支持通配符订阅的类型安全 EventBus。
// ✅ 通配符匹配类型
type WildcardMatch<Pattern extends string, Event extends string> =
  Pattern extends "*"
    ? Event
    : Pattern extends `${infer Prefix}*`
      ? Event extends `${Prefix}${infer _}`
        ? Event
        : never
      : Pattern extends Event
        ? Event
        : never;

// ✅ 从事件映射中提取匹配的事件
type MatchingEvents<
  Pattern extends string,
  EventMap extends Record<string, any>
> = {
  [K in keyof EventMap]: WildcardMatch<Pattern, K & string> extends never
    ? never
    : K;
}[keyof EventMap];

// ✅ 通配符 EventBus
class WildcardEventBus<EventMap extends Record<string, any>> {
  private listeners = new Map<string, Set<(payload: any) => void>>();

  on<Pattern extends string>(
    pattern: Pattern,
    callback: (event: MatchingEvents<Pattern, EventMap>, payload: EventMap[MatchingEvents<Pattern, EventMap>]) => void
  ): () => void {
    // 存储监听器
    const wrappedCallback = (eventName: string, payload: any) => {
      if (this.matchPattern(pattern, eventName)) {
        callback(eventName as any, payload);
      }
    };

    if (!this.listeners.has(pattern)) {
      this.listeners.set(pattern, new Set());
    }
    this.listeners.get(pattern)!.add(wrappedCallback);

    // 返回取消订阅函数
    return () => {
      this.listeners.get(pattern)?.delete(wrappedCallback);
    };
  }

  emit<K extends keyof EventMap & string>(event: K, payload: EventMap[K]): void {
    for (const [pattern, callbacks] of this.listeners) {
      if (this.matchPattern(pattern, event)) {
        callbacks.forEach(cb => cb(event, payload));
      }
    }
  }

  private matchPattern(pattern: string, event: string): boolean {
    if (pattern === "*") return true;
    if (pattern.endsWith("*")) {
      return event.startsWith(pattern.slice(0, -1));
    }
    return pattern === event;
  }
}

// ✅ 使用
interface AppEvents {
  "user:login": { userId: string };
  "user:logout": { userId: string };
  "user:update": { userId: string; changes: string[] };
  "order:create": { orderId: string };
  "order:cancel": { orderId: string };
  "notification:send": { message: string };
}

const bus = new WildcardEventBus<AppEvents>();

// 订阅所有 user 事件
bus.on("user:*", (event, payload) => {
  console.log(`用户事件: ${event}`, payload);
  // event: "user:login" | "user:logout" | "user:update"
});

// 订阅所有事件
bus.on("*", (event, payload) => {
  console.log(`所有事件: ${event}`);
  // event: "user:login" | "user:logout" | "user:update" | "order:create" | ...
});

// 订阅特定事件
bus.on("order:create", (event, payload) => {
  console.log("订单创建:", payload.orderId);
});

bus.emit("user:login", { userId: "1" });    // 触发 "user:*" 和 "*"
bus.emit("order:create", { orderId: "O1" }); // 触发 "order:create" 和 "*"
考察点:
  1. 模
板字面量类型匹配
  1. 条
件类型过滤
  1. 类
型安全的事件系统
  1. 通
配符模式设计

面试题 12:实现一个类型安全的 CSS-in-JS 系统

题目:实现一个类型安全的 CSS 属性生成器,支持嵌套、伪类和响应式。
// ✅ CSS 属性类型
type CSSValue = string | number;

interface CSSProperties {
  color?: CSSValue;
  backgroundColor?: CSSValue;
  fontSize?: CSSValue;
  margin?: CSSValue;
  padding?: CSSValue;
  display?: "flex" | "block" | "inline" | "none";
  flexDirection?: "row" | "column";
  // ... 更多属性
}

// ✅ 伪类和嵌套
type PseudoSelector = ":hover" | ":focus" | ":active" | ":first-child" | ":last-child";
type MediaQuery = "@media (min-width: 768px)" | "@media (min-width: 1024px)";

type CSSBlock<Props extends CSSProperties> = {
  [K in keyof Props]?: Props[K];
};

type CSSWithNesting<Props extends CSSProperties> = CSSBlock<Props> & {
  [K in PseudoSelector]?: CSSBlock<Props>;
} & {
  [K in MediaQuery]?: CSSBlock<Props>;
} & {
  [K in `& ${string}`]?: CSSBlock<Props>;
};

// ✅ 类型安全的 css 函数
function css<Props extends CSSProperties>(
  styles: CSSWithNesting<Props>
): string {
  // 简化实现:将对象转为 CSS 字符串
  let result = "";
  for (const [key, value] of Object.entries(styles)) {
    if (typeof value === "object") {
      result += `${key} { ${css(value as any)} }`;
    } else {
      const prop = key.replace(/([A-Z])/g, "-$1").toLowerCase();
      result += `${prop}: ${value};`;
    }
  }
  return result;
}

// ✅ 使用
const buttonStyles = css({
  color: "#fff",
  backgroundColor: "#007bff",
  padding: "8px 16px",
  fontSize: 14,
  ":hover": {
    backgroundColor: "#0056b3",
  },
  ":active": {
    backgroundColor: "#004085",
  },
  "@media (min-width: 768px)": {
    padding: "12px 24px",
    fontSize: 16,
  },
});

// ❌ 类型错误
// const badStyles = css({
//   colour: "red",          // ❌ 拼写错误
//   display: "grid",        // ❌ 不在允许的值中
//   ":hovered": {},         // ❌ 不存在的伪类
// });
考察点:
  1. 映
射类型约束属性
  1. 模
板字面量类型定义伪类
  1. 递
归类型处理嵌套
  1. 类
型安全的 DSL 设计

面试题 13:实现一个类型安全的 DI 容器(进阶版)

题目:实现支持作用域(Singleton/Transient/Scoped)的依赖注入容器。
import "reflect-metadata";

// ============ 类型定义 ============
type Constructor<T = {}> = new (...args: any[]) => T;
type Lifetime = "singleton" | "transient" | "scoped";

interface Registration<T> {
  token: string;
  implementation: Constructor<T>;
  lifetime: Lifetime;
  instance?: T;
}

// ============ 容器实现 ============
class DIContainer {
  private registrations = new Map<string, Registration<any>>();
  private scopedInstances = new Map<string, any>();

  register<T>(
    token: string,
    implementation: Constructor<T>,
    lifetime: Lifetime = "transient"
  ): this {
    this.registrations.set(token, { token, implementation, lifetime });
    return this;
  }

  registerSingleton<T>(token: string, implementation: Constructor<T>): this {
    return this.register(token, implementation, "singleton");
  }

  registerTransient<T>(token: string, implementation: Constructor<T>): this {
    return this.register(token, implementation, "transient");
  }

  resolve<T>(token: string): T {
    const registration = this.registrations.get(token);
    if (!registration) {
      throw new Error(`未注册: ${token}`);
    }

    const { implementation, lifetime, instance } = registration;

    switch (lifetime) {
      case "singleton":
        if (!instance) {
          registration.instance = this.createInstance<T>(implementation);
        }
        return registration.instance!;

      case "scoped":
        if (!this.scopedInstances.has(token)) {
          this.scopedInstances.set(token, this.createInstance<T>(implementation));
        }
        return this.scopedInstances.get(token)!;

      case "transient":
      default:
        return this.createInstance<T>(implementation);
    }
  }

  private createInstance<T>(impl: Constructor<T>): T {
    const paramTypes: Constructor[] =
      Reflect.getMetadata("design:paramtypes", impl) || [];

    const args = paramTypes.map((type, index) => {
      // 尝试按类型名解析
      const token = type.name;
      try {
        return this.resolve(token);
      } catch {
        return undefined;
      }
    });

    return new impl(...args);
  }

  createScope(): DIContainer {
    const child = new DIContainer();
    child.registrations = new Map(this.registrations);
    return child;
  }
}

// ============ 使用示例 ============
interface ILogger {
  log(msg: string): void;
}

class ConsoleLogger implements ILogger {
  log(msg: string) {
    console.log(`[LOG] ${msg}`);
  }
}

interface IUserRepository {
  getUsers(): string[];
}

class UserRepository implements IUserRepository {
  getUsers() {
    return ["Alice", "Bob"];
  }
}

class UserService {
  constructor(
    private logger: ILogger,
    private userRepo: IUserRepository
  ) {}

  listUsers() {
    this.logger.log("获取用户列表");
    return this.userRepo.getUsers();
  }
}

// 注册
const container = new DIContainer();
container.registerSingleton("ILogger", ConsoleLogger);
container.registerTransient("IUserRepository", UserRepository);
container.registerTransient("UserService", UserService);

// 使用
const userService = container.resolve<UserService>("UserService");
console.log(userService.listUsers());
// [LOG] 获取用户列表
// ["Alice", "Bob"]

// 验证 Singleton
const logger1 = container.resolve<ILogger>("ILogger");
const logger2 = container.resolve<ILogger>("ILogger");
console.log(logger1 === logger2); // true

// 验证 Transient
const repo1 = container.resolve<IUserRepository>("IUserRepository");
const repo2 = container.resolve<IUserRepository>("IUserRepository");
console.log(repo1 === repo2); // false
考察点:
  1. 装
饰器与反射元数据
  1. 生
命周期管理
  1. 递
归依赖解析
  1. 作
用域设计模式

面试题 14:实现一个类型安全的 Query Builder

题目:实现一个类型安全的 SQL 查询构建器。
// ✅ 表结构到类型的映射
type TableSchema = Record<string, Record<string, "string" | "number" | "boolean" | "Date">>;

type ColumnType = "string" | "number" | "boolean" | "Date";
type JsType<T extends ColumnType> =
  T extends "string" ? string :
  T extends "number" ? number :
  T extends "boolean" ? boolean :
  T extends "Date" ? Date :
  never;

// ✅ 将表 Schema 转为 TypeScript 类型
type TableType<Schema extends Record<string, ColumnType>> = {
  [K in keyof Schema]: JsType<Schema[K]>;
};

// ✅ Where 条件类型
type WhereCondition<T> = {
  [K in keyof T]?: T[K] | T[K][] | { $gt?: T[K]; $lt?: T[K]; $gte?: T[K]; $lte?: T[K]; $ne?: T[K] };
};

// ✅ OrderBy 类型
type OrderBy<T> = {
  [K in keyof T]?: "ASC" | "DESC";
};

// ✅ QueryBuilder 类
class QueryBuilder<
  TableName extends string,
  Schema extends Record<string, ColumnType>,
  Result = TableType<Schema>
> {
  private tableName: TableName;
  private selectedFields: (keyof Schema)[] = [];
  private whereConditions: WhereCondition<TableType<Schema>>[] = [];
  private orderByFields: OrderBy<TableType<Schema>> = {};
  private limitCount?: number;
  private offsetCount?: number;

  constructor(tableName: TableName) {
    this.tableName = tableName;
  }

  select<K extends keyof Schema>(...fields: K[]): QueryBuilder<TableName, Schema, Pick<TableType<Schema>, K>> {
    this.selectedFields = fields as any;
    return this as any;
  }

  where(condition: WhereCondition<TableType<Schema>>): this {
    this.whereConditions.push(condition);
    return this;
  }

  orderBy(order: OrderBy<TableType<Schema>>): this {
    this.orderByFields = order;
    return this;
  }

  limit(count: number): this {
    this.limitCount = count;
    return this;
  }

  offset(count: number): this {
    this.offsetCount = count;
    return this;
  }

  toSQL(): string {
    const fields = this.selectedFields.length > 0
      ? this.selectedFields.join(", ")
      : "*";

    let sql = `SELECT ${fields} FROM ${this.tableName}`;

    if (this.whereConditions.length > 0) {
      const whereClause = this.whereConditions
        .map(cond => Object.entries(cond)
          .map(([k, v]) => {
            if (Array.isArray(v)) return `${k} IN (${v.map(x => `'${x}'`).join(", ")})`;
            if (typeof v === "object" && v !== null) {
              const parts = Object.entries(v).map(([op, val]) => {
                const operator = { $gt: ">", $lt: "<", $gte: ">=", $lte: "<=", $ne: "!=" }[op];
                return `${k} ${operator} '${val}'`;
              });
              return parts.join(" AND ");
            }
            return `${k} = '${v}'`;
          })
          .join(" AND "))
        .join(" AND ");
      sql += ` WHERE ${whereClause}`;
    }

    const orderEntries = Object.entries(this.orderByFields);
    if (orderEntries.length > 0) {
      sql += ` ORDER BY ${orderEntries.map(([k, v]) => `${k} ${v}`).join(", ")}`;
    }

    if (this.limitCount !== undefined) sql += ` LIMIT ${this.limitCount}`;
    if (this.offsetCount !== undefined) sql += ` OFFSET ${this.offsetCount}`;

    return sql;
  }
}

// ✅ 使用
interface UserTable {
  id: "number";
  name: "string";
  email: "string";
  age: "number";
  isActive: "boolean";
}

// 创建查询
const query = new QueryBuilder<"users", UserTable>("users")
  .select("id", "name", "email")
  .where({ age: { $gte: 18, $lte: 65 } })
  .where({ isActive: true })
  .orderBy({ name: "ASC" })
  .limit(10)
  .offset(0);

console.log(query.toSQL());
// SELECT id, name, email FROM users WHERE age >= '18' AND age <= '65' AND isActive = 'true' ORDER BY name ASC LIMIT 10 OFFSET 0

// select 后结果类型变化
const query2 = new QueryBuilder<"users", UserTable>("users")
  .select("id", "name");
// query2 的结果类型: { id: number; name: string }
考察点:
  1. 泛
型状态变化追踪
  1. 条
件类型映射
  1. 链
式调用的类型安全
  1. 类
型级别的 Schema 到类型转换

面试题 15:实现一个类型安全的状态机

题目:实现一个编译时检查的状态机类型。
// ✅ 状态机类型定义
type StateDefinition<
  State extends string,
  Event extends string,
  Transitions extends Record<State, Partial<Record<Event, State>>>
> = {
  initial: State;
  transitions: Transitions;
};

// ✅ 状态机类型
type StateMachine<
  S extends string,
  E extends string,
  T extends Record<S, Partial<Record<E, S>>>
> = {
  state: S;
  send: <Ev extends keyof T[S]>(
    event: Ev
  ) => StateMachine<S, E, T>;
};

// ✅ 创建状态机
function createStateMachine<
  S extends string,
  E extends string,
  T extends Record<S, Partial<Record<E, S>>>
>(
  _config: { initial: S; transitions: T }
): StateMachine<S, E, T> {
  return {
    state: _config.initial,
    send(event: E) {
      const nextState = _config.transitions[this.state as S]?.[event as any];
      if (!nextState) {
        throw new Error(`无效转换: ${this.state} -> ${event}`);
      }
      return { ...this, state: nextState } as any;
    },
  };
}

// ✅ 使用:红绿灯
type TrafficLightState = "green" | "yellow" | "red";
type TrafficLightEvent = "timer" | "emergency";

const trafficLight = createStateMachine<TrafficLightState, TrafficLightEvent, {
  green: { timer: "yellow"; emergency: "red" };
  yellow: { timer: "red"; emergency: "red" };
  red: { timer: "green"; emergency: "red" };
}>({
  initial: "green",
  transitions: {
    green: { timer: "yellow", emergency: "red" },
    yellow: { timer: "red", emergency: "red" },
    red: { timer: "green", emergency: "red" },
  },
});

// 状态转换
let light = trafficLight;
light = light.send("timer");    // green -> yellow
light = light.send("timer");    // yellow -> red
light = light.send("timer");    // red -> green
light = light.send("emergency"); // green -> red

// ✅ 使用:订单状态
type OrderState = "pending" | "paid" | "shipped" | "delivered" | "cancelled";
type OrderEvent = "pay" | "ship" | "deliver" | "cancel";

const orderMachine = createStateMachine<OrderState, OrderEvent, {
  pending: { pay: "paid"; cancel: "cancelled" };
  paid: { ship: "shipped"; cancel: "cancelled" };
  shipped: { deliver: "delivered" };
  delivered: {};
  cancelled: {};
}>({
  initial: "pending",
  transitions: {
    pending: { pay: "paid", cancel: "cancelled" },
    paid: { ship: "shipped", cancel: "cancelled" },
    shipped: { deliver: "delivered" },
    delivered: {},
    cancelled: {},
  },
});

let order = orderMachine;
order = order.send("pay");     // pending -> paid
order = order.send("ship");    // paid -> shipped
order = order.send("deliver"); // shipped -> delivered

// order.send("cancel"); // ❌ 编译错误:delivered 状态不允许 cancel 事件
考察点:
  1. 泛
型约束状态和事件
  1. 类
型级别的状态转换检查
  1. 不
可达状态的编译时检测
  1. 类
型安全的状态机模式

总结

类型系统核心要点

概念核心能力面试频率
条件类型类型级别的 if-else⭐⭐⭐⭐⭐
infer类型提取与推断⭐⭐⭐⭐⭐
映射类型类型转换与重构⭐⭐⭐⭐⭐
模板字面量类型字符串类型操作⭐⭐⭐⭐
分布式条件类型联合类型分发⭐⭐⭐⭐
Key Remapping键名重映射⭐⭐⭐⭐
递归类型处理嵌套结构⭐⭐⭐⭐

装饰器核心要点

装饰器类型核心能力面试频率
类装饰器混入、单例、元数据⭐⭐⭐⭐⭐
方法装饰器计时、缓存、权限、重试⭐⭐⭐⭐⭐
属性装饰器验证、日志、代理⭐⭐⭐⭐
参数装饰器依赖注入、参数校验⭐⭐⭐
装饰器工厂参数化装饰器⭐⭐⭐⭐

学习建议

  1. <
strong>先掌握基础类型操作:联合、交叉、keyof、typeof、索引访问
  1. <
strong>深入理解条件类型:分布式特性、infer 的各种用法
  1. <
strong>练习类型体操:从简单的工具类型实现开始,逐步挑战复杂类型
  1. <
strong>理解装饰器原理:从执行顺序到元数据反射
  1. <
strong>结合实际项目:在 NestJS、Vue、React 等框架中应用这些知识