TypeScript 类型进阶与面试题
- [第一部分:TypeScript 类型系统深入](#第一部分typescript-类型系统深入)
前端架构师通关手册 · 专题 22
目录
- [第一部分:TypeScript 类型系统深入](#第一部分typescript-类型系统深入)
- [1.1 高级类型操作](#11-高级类型操作)
- [1.2 条件类型与 infer](#12-条件类型与-infer)
- [1.3 映射类型与模板字面量类型](#13-映射类型与模板字面量类型)
- [1.4 内置工具类型详解](#14-内置工具类型详解)
- [第二部分:TypeScript 装饰器](#第二部分typescript-装饰器)
- [2.1 装饰器基础](#21-装饰器基础)
- [2.2 类装饰器](#22-类装饰器)
- [2.3 方法装饰器](#23-方法装饰器)
- [2.4 属性装饰器](#24-属性装饰器)
- [2.5 参数装饰器](#25-参数装饰器)
- [2.6 装饰器工厂与执行顺序](#26-装饰器工厂与执行顺序)
- [第三部分:实际应用](#第三部分实际应用)
- [3.1 实战:构建 Validation 装饰器](#31-实战构建-validation-装饰器)
- [3.2 实战:实现依赖注入](#32-实战实现依赖注入)
- [3.3 实战:Vue/React 中的类型体操](#33-实战vue--react-中的类型体操)
- [第四部分:经典面试题与详解](#第四部分经典面试题与详解)
第一部分: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" ,因为它是只读属性
考察点:
- 递
归类型的理解
- 函
数类型的判断(
T extends Function)
- 映
射类型 + 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;
};
考察点:
- K
ey Remapping(
as 子句)
- 条
件类型的分布特性
- 递
归类型应用
面试题 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 }
考察点:
- 条
件类型的分布式特性
- 函
数参数的逆变(contravariance)
- <
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", {}); // ❌ 事件名不存在
考察点:
- 泛
型约束与索引类型
- 映
射类型与
keyof 的配合
- 类
型安全的 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"); // ❌ 编译错误
考察点:
- 模
板字面量类型
- 递
归类型
- 条
件类型中的字符串推断
- 类
型安全的运行时函数
面试题 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>
考察点:
- 泛
型状态追踪
- <
code>this 参数类型约束
- 条
件返回类型
- 类
型级别的"状态机"
面试题 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
考察点:
- 装
饰器工厂模式
- <
code>reflect-metadata 的使用
- 元
数据的设计模式
- 类
装饰器 + 方法装饰器配合
面试题 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" // 原对象不变
考察点:
- 模
板字面量类型拆分路径
- 递
归类型 +
Omit 组合
- 不
可变数据更新模式
- 类
型与运行时的一致性
面试题 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
考察点:
- 复
杂泛型约束
- <
code>ThisType 的使用
- 类
型提取(Payload 推断)
- 类
型安全的 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);
}
考察点:
- 条
件类型的链式判断
- 递
归类型处理嵌套结构
- <
code>as const 与字面量类型收窄
- 类
型与运行时验证的一致性
面试题 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" 和 "*"
考察点:
- 模
板字面量类型匹配
- 条
件类型过滤
- 类
型安全的事件系统
- 通
配符模式设计
面试题 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": {}, // ❌ 不存在的伪类
// });
考察点:
- 映
射类型约束属性
- 模
板字面量类型定义伪类
- 递
归类型处理嵌套
- 类
型安全的 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
考察点:
- 装
饰器与反射元数据
- 生
命周期管理
- 递
归依赖解析
- 作
用域设计模式
面试题 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 }
考察点:
- 泛
型状态变化追踪
- 条
件类型映射
- 链
式调用的类型安全
- 类
型级别的 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 事件
考察点:
- 泛
型约束状态和事件
- 类
型级别的状态转换检查
- 不
可达状态的编译时检测
- 类
型安全的状态机模式
总结
类型系统核心要点
| 概念 | 核心能力 | 面试频率 |
|---|
| 条件类型 | 类型级别的 if-else | ⭐⭐⭐⭐⭐ |
| infer | 类型提取与推断 | ⭐⭐⭐⭐⭐ |
| 映射类型 | 类型转换与重构 | ⭐⭐⭐⭐⭐ |
| 模板字面量类型 | 字符串类型操作 | ⭐⭐⭐⭐ |
| 分布式条件类型 | 联合类型分发 | ⭐⭐⭐⭐ |
| Key Remapping | 键名重映射 | ⭐⭐⭐⭐ |
| 递归类型 | 处理嵌套结构 | ⭐⭐⭐⭐ |
装饰器核心要点
| 装饰器类型 | 核心能力 | 面试频率 |
|---|
| 类装饰器 | 混入、单例、元数据 | ⭐⭐⭐⭐⭐ |
| 方法装饰器 | 计时、缓存、权限、重试 | ⭐⭐⭐⭐⭐ |
| 属性装饰器 | 验证、日志、代理 | ⭐⭐⭐⭐ |
| 参数装饰器 | 依赖注入、参数校验 | ⭐⭐⭐ |
| 装饰器工厂 | 参数化装饰器 | ⭐⭐⭐⭐ |
学习建议
- <
strong>先掌握基础类型操作:联合、交叉、keyof、typeof、索引访问
- <
strong>深入理解条件类型:分布式特性、infer 的各种用法
- <
strong>练习类型体操:从简单的工具类型实现开始,逐步挑战复杂类型
- <
strong>理解装饰器原理:从执行顺序到元数据反射
- <
strong>结合实际项目:在 NestJS、Vue、React 等框架中应用这些知识