主题
阶段 3 练习题集合
本章包含 20-25 个精心设计的练习题,涵盖 TypeScript 高级类型、装饰器、设计模式、架构、性能优化和测试。
📚 练习分类
- TypeScript 高级类型练习 (6题)
- 装饰器练习 (4题)
- 设计模式练习 (6题)
- 架构和性能练习 (5题)
- 测试练习 (4题)
1. TypeScript 高级类型练习
练习 1.1:类型安全的事件发射器
难度: ⭐⭐⭐⭐
要求: 实现一个类型安全的事件发射器,确保事件名称和处理器参数类型匹配。
typescript
// 定义事件映射
interface EventMap {
'user:login': { userId: string; timestamp: number };
'user:logout': { userId: string };
'data:update': { id: string; data: any };
}
// TODO: 实现 EventEmitter 类
class EventEmitter<T extends Record<string, any>> {
// 你的实现...
}
// 测试
const emitter = new EventEmitter<EventMap>();
// ✓ 应该通过类型检查
emitter.on('user:login', (payload) => {
console.log(payload.userId, payload.timestamp);
});
// ❌ 应该报错:参数类型不匹配
// emitter.on('user:login', (payload: { wrong: string }) => {});<details> <summary>查看答案</summary>
typescript
class EventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: Array<(payload: T[K]) => void>;
} = {};
on<K extends keyof T>(event: K, listener: (payload: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
emit<K extends keyof T>(event: K, payload: T[K]): void {
const eventListeners = this.listeners[event];
if (eventListeners) {
eventListeners.forEach(listener => listener(payload));
}
}
off<K extends keyof T>(event: K, listener: (payload: T[K]) => void): void {
const eventListeners = this.listeners[event];
if (eventListeners) {
const index = eventListeners.indexOf(listener);
if (index !== -1) {
eventListeners.splice(index, 1);
}
}
}
}</details>
练习 1.2:深度只读类型
难度: ⭐⭐⭐⭐
要求: 实现一个 DeepReadonly<T> 类型,递归地将对象的所有嵌套属性设为只读。
typescript
// TODO: 实现 DeepReadonly 类型
interface NestedObject {
a: string;
b: {
c: number;
d: {
e: boolean;
};
};
f: string[];
}
type ReadonlyNested = DeepReadonly<NestedObject>;<details> <summary>查看答案</summary>
typescript
type DeepReadonly<T> = T extends (infer R)[]
? ReadonlyArray<DeepReadonly<R>>
: T extends Function
? T
: T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;</details>
2. 装饰器练习
练习 2.1:速率限制装饰器
难度: ⭐⭐⭐⭐
要求: 创建一个 @rateLimit 装饰器,限制方法在指定时间内的调用次数。
typescript
class ApiClient {
@rateLimit(5, 1000) // 每秒最多 5 次调用
async makeRequest(url: string) {
return fetch(url);
}
}<details> <summary>查看答案</summary>
typescript
function rateLimit(maxCalls: number, timeWindow: number) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
const calls: number[] = [];
descriptor.value = async function(...args: any[]) {
const now = Date.now();
// 移除过期的调用记录
while (calls.length > 0 && calls[0] < now - timeWindow) {
calls.shift();
}
// 检查是否超过限制
if (calls.length >= maxCalls) {
throw new Error(
`速率限制: 最多 ${maxCalls} 次调用每 ${timeWindow}ms`
);
}
// 记录此次调用
calls.push(now);
// 执行原始方法
return await originalMethod.apply(this, args);
};
return descriptor;
};
}</details>
3. 设计模式练习
练习 3.1:实现观察者模式
难度: ⭐⭐⭐
要求: 实现一个简单的观察者模式,支持订阅、取消订阅和通知。
typescript
// TODO: 实现观察者模式
interface Observer {
update(data: any): void;
}
interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(data: any): void;
}<details> <summary>查看答案</summary>
typescript
class ConcreteSubject implements Subject {
private observers: Observer[] = [];
attach(observer: Observer): void {
if (!this.observers.includes(observer)) {
this.observers.push(observer);
}
}
detach(observer: Observer): void {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}
notify(data: any): void {
this.observers.forEach(observer => {
observer.update(data);
});
}
}
class ConcreteObserver implements Observer {
constructor(private name: string) {}
update(data: any): void {
console.log(`${this.name} 收到更新:`, data);
}
}</details>
4. 架构和性能练习
练习 4.1:实现 LRU 缓存
难度: ⭐⭐⭐⭐
要求: 实现一个 LRU(最近最少使用)缓存。
typescript
class LRUCache<K, V> {
constructor(private maxSize: number) {}
get(key: K): V | undefined {
// TODO: 实现
}
set(key: K, value: V): void {
// TODO: 实现
}
}<details> <summary>查看答案</summary>
typescript
class LRUCache<K, V> {
private cache = new Map<K, V>();
constructor(private maxSize: number) {}
get(key: K): V | undefined {
if (!this.cache.has(key)) return undefined;
const value = this.cache.get(key)!;
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
set(key: K, value: V): void {
if (this.cache.has(key)) {
this.cache.delete(key);
}
this.cache.set(key, value);
if (this.cache.size > this.maxSize) {
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
}
}</details>
5. 测试练习
练习 5.1:编写单元测试
难度: ⭐⭐⭐
要求: 为以下用户服务编写单元测试。
typescript
class UserService {
constructor(private repository: UserRepository) {}
async createUser(data: CreateUserData): Promise<User> {
if (!data.email || !data.name) {
throw new Error('邮箱和姓名是必需的');
}
const user = await this.repository.save(data);
return user;
}
}
// TODO: 编写测试
describe('UserService', () => {
// 你的测试...
});<details> <summary>查看答案</summary>
typescript
describe('UserService', () => {
let service: UserService;
let mockRepository: jest.Mocked<UserRepository>;
beforeEach(() => {
mockRepository = {
save: jest.fn()
} as any;
service = new UserService(mockRepository);
});
it('应该创建用户', async () => {
const userData = {
name: 'Alice',
email: 'alice@example.com'
};
const expectedUser = {
id: '1',
...userData
};
mockRepository.save.mockResolvedValue(expectedUser);
const user = await service.createUser(userData);
expect(user).toEqual(expectedUser);
expect(mockRepository.save).toHaveBeenCalledWith(userData);
});
it('应该在缺少必需字段时抛出错误', async () => {
await expect(
service.createUser({ email: '', name: 'Alice' })
).rejects.toThrow('邮箱和姓名是必需的');
});
});</details>
新增练习题详解
练习 6.1:Proxy 响应式对象(高级)
难度:⭐⭐⭐⭐⭐
要求:用 Proxy 实现一个可观察对象,当属性变化时自动通知观察者
typescript
/**
* 实现 observable(obj, onChange) 函数
* - 当对象的任何属性改变时,调用 onChange
* - 支持嵌套对象的监听
* - 支持数组操作监听
*/<details> <summary>参考答案</summary>
核心思路:
- 使用 Proxy 拦截 get/set 操作
- 递归代理嵌套对象
- 使用 Reflect 调用默认行为
- 实现依赖收集和触发更新
</details>
练习 7.1:函数式编程工具(高级)
难度:⭐⭐⭐⭐
要求:实现 pipe() + compose() + curry() 三个函数
练习 8.1:Web Worker 图片处理(中高级)
难度:⭐⭐⭐⭐
要求:用 Web Worker 实现图片灰度化处理
练习 9.1:编写声明文件(中高级)
难度:⭐⭐⭐⭐
要求:为无类型 npm 包编写 .d.ts 声明文件
自我评估
完成练习后,请检查:
- [ ] 完成了至少 80% 的练习题
- [ ] 理解了所有练习的解决方案
- [ ] 能够独立解决类似问题
- [ ] 掌握了相关的设计模式和最佳实践
- [ ] 理解 Proxy 和元编程的应用场景
- [ ] 能够编写函数式风格的代码
- [ ] 掌握 Web Worker 的使用
- [ ] 能够为第三方库编写类型声明
🆕 Proxy 与 Reflect 练习
练习 6.1: 实现响应式对象(基础)
难度: ⭐⭐⭐ 涉及知识点: Proxy、Reflect、响应式系统基础 预计时间: 30分钟
题目描述:
实现一个简化版的 Vue 3 reactive() 函数,当对象属性变化时触发回调。
要求:
- 使用 Proxy 拦截对象的 get 和 set 操作
- 实现
reactive(obj)函数创建响应式对象 - 实现
watch(obj, callback)函数监听变化 - 支持嵌套对象的响应式
- 添加日志记录所有拦截操作
提示: 使用 WeakMap 存储对象和观察者的映射
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/06-reactive-proxy.ts
思路说明:
- Proxy 的 get trap 追踪属性访问
- set trap 触发更新通知
- 递归处理嵌套对象
关键代码:
typescript
const observers = new WeakMap<object, Set<Function>>();
function reactive<T extends object>(obj: T): T {
const observed = observers.get(obj) || new Set();
observers.set(obj, observed);
return new Proxy(obj, {
get(target, prop, receiver) {
console.log(`[GET] ${String(prop)}`);
const value = Reflect.get(target, prop, receiver);
// 递归处理嵌套对象
if (value !== null && typeof value === 'object') {
return reactive(value);
}
return value;
},
set(target, prop, value, receiver) {
const oldValue = Reflect.get(target, prop, receiver);
const result = Reflect.set(target, prop, value, receiver);
if (oldValue !== value) {
console.log(`[SET] ${String(prop)}: ${oldValue} → ${value}`);
// 触发观察者
observed.forEach(cb => cb(prop, value, oldValue));
}
return result;
}
});
}
function watch<T extends object>(obj: T, callback: Function): void {
const observed = observers.get(obj);
if (observed) {
observed.add(callback);
}
}
// 测试
const state = reactive({ count: 0, user: { name: 'Alice' } });
watch(state, (prop: string, newVal: any, oldVal: any) => {
console.log(`属性 ${prop} 变化: ${oldVal} → ${newVal}`);
});
state.count++; // 触发回调
state.user.name = 'Bob'; // 嵌套对象也能响应</details>
练习 6.2: 数据验证代理(进阶)
难度: ⭐⭐⭐⭐ 涉及知识点: Proxy、Reflect、运行时验证 预计时间: 35分钟
题目描述:
创建一个 Proxy,在设置属性时自动进行类型和值验证。
要求:
- 实现
createValidator(schema)函数 - 支持类型检查(string、number、boolean 等)
- 支持值约束(min、max、pattern 等)
- 设置无效值时抛出描述性错误
- 支持可选属性和默认值
提示: 在 set trap 中执行验证逻辑
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/06-validation-proxy.ts
思路说明:
- Schema 定义验证规则
- Proxy set trap 执行验证
- 提供清晰的错误信息
关键代码:
typescript
interface ValidationRule {
type?: 'string' | 'number' | 'boolean';
required?: boolean;
min?: number;
max?: number;
pattern?: RegExp;
default?: any;
}
type Schema = Record<string, ValidationRule>;
function createValidator<T extends object>(schema: Schema): T {
const obj: any = {};
// 设置默认值
for (const [key, rule] of Object.entries(schema)) {
if (rule.default !== undefined) {
obj[key] = rule.default;
}
}
return new Proxy(obj, {
set(target, prop, value) {
const propName = String(prop);
const rule = schema[propName];
if (!rule) {
throw new Error(`未定义的属性: ${propName}`);
}
// 必填检查
if (rule.required && (value === undefined || value === null)) {
throw new Error(`${propName} 是必填项`);
}
// 类型检查
if (rule.type && typeof value !== rule.type) {
throw new Error(
`${propName} 类型错误: 期望 ${rule.type}, 实际 ${typeof value}`
);
}
// 数值范围检查
if (rule.type === 'number') {
if (rule.min !== undefined && value < rule.min) {
throw new Error(`${propName} 不能小于 ${rule.min}`);
}
if (rule.max !== undefined && value > rule.max) {
throw new Error(`${propName} 不能大于 ${rule.max}`);
}
}
// 正则匹配检查
if (rule.pattern && !rule.pattern.test(value)) {
throw new Error(`${propName} 格式无效`);
}
console.log(`✓ 验证通过: ${propName} = ${value}`);
return Reflect.set(target, prop, value);
}
});
}
// 使用
const user = createValidator<any>({
name: { type: 'string', required: true },
age: { type: 'number', min: 0, max: 150 },
email: { type: 'string', pattern: /^[\w-]+@[\w-]+\.\w+$/ }
});
user.name = 'Alice'; // ✓
user.age = 25; // ✓
// user.age = -1; // ✗ 抛出错误</details>
练习 6.3: 实现可撤销的 Proxy(挑战)
难度: ⭐⭐⭐⭐⭐ 涉及知识点: Proxy、Reflect、Proxy.revocable、内存管理 预计时间: 40分钟
题目描述:
实现一个支持时间旅行(撤销/重做)的对象代理系统。
要求:
- 记录所有属性变更历史
- 实现
undo()撤销最近的修改 - 实现
redo()重做被撤销的修改 - 实现
getHistory()获取完整历史 - 支持跳转到任意历史状态
- 添加内存优化(限制历史记录数量)
提示: 使用栈结构存储历史状态
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/06-time-travel-proxy.ts
思路说明:
- 维护两个栈:历史栈和重做栈
- 每次修改前保存快照
- 撤销时恢复快照并移到重做栈
关键代码:
typescript
interface HistoryEntry<T> {
timestamp: Date;
snapshot: T;
changes: Array<{ prop: string; from: any; to: any }>;
}
function createTimeTravelProxy<T extends object>(
initialState: T,
maxHistory: number = 50
) {
let state = { ...initialState };
const history: HistoryEntry<T>[] = [
{ timestamp: new Date(), snapshot: { ...state }, changes: [] }
];
let historyIndex = 0;
const proxy = new Proxy(state, {
set(target, prop, value) {
const oldValue = Reflect.get(target, prop);
if (oldValue === value) return true;
// 清除重做栈
history.splice(historyIndex + 1);
// 保存新状态
Reflect.set(target, prop, value);
const snapshot = { ...target } as T;
history.push({
timestamp: new Date(),
snapshot,
changes: [{ prop: String(prop), from: oldValue, to: value }]
});
// 限制历史记录数量
if (history.length > maxHistory) {
history.shift();
} else {
historyIndex++;
}
console.log(`[变更] ${String(prop)}: ${oldValue} → ${value}`);
return true;
}
});
return {
proxy,
undo(): boolean {
if (historyIndex === 0) {
console.log('[撤销失败] 已到达历史起点');
return false;
}
historyIndex--;
const entry = history[historyIndex];
Object.assign(state, entry.snapshot);
console.log(`[撤销] 返回到 ${entry.timestamp.toISOString()}`);
return true;
},
redo(): boolean {
if (historyIndex >= history.length - 1) {
console.log('[重做失败] 已到达历史终点');
return false;
}
historyIndex++;
const entry = history[historyIndex];
Object.assign(state, entry.snapshot);
console.log(`[重做] 前进到 ${entry.timestamp.toISOString()}`);
return true;
},
getHistory() {
return history.map((entry, idx) => ({
...entry,
isCurrent: idx === historyIndex
}));
},
jumpTo(index: number): boolean {
if (index < 0 || index >= history.length) {
console.log('[跳转失败] 索引越界');
return false;
}
historyIndex = index;
const entry = history[index];
Object.assign(state, entry.snapshot);
console.log(`[跳转] 到达 ${entry.timestamp.toISOString()}`);
return true;
}
};
}
// 测试
const { proxy, undo, redo, getHistory } = createTimeTravelProxy({
count: 0,
name: 'Alice'
});
proxy.count = 1;
proxy.count = 2;
proxy.name = 'Bob';
console.log('当前状态:', proxy); // { count: 2, name: 'Bob' }
undo();
console.log('撤销后:', proxy); // { count: 2, name: 'Alice' }
redo();
console.log('重做后:', proxy); // { count: 2, name: 'Bob' }</details>
🆕 函数式编程练习
练习 7.1: 实现 pipe 和 compose(基础)
难度: ⭐⭐⭐ 涉及知识点: 函数组合、高阶函数、类型推导 预计时间: 25分钟
题目描述:
实现函数式编程的核心工具:pipe 和 compose。
要求:
- 实现
pipe(...fns)从左到右组合函数 - 实现
compose(...fns)从右到左组合函数 - 支持任意数量的函数
- 保持类型安全(TypeScript)
- 添加示例:数据转换管道
提示: pipe 和 compose 的区别在于执行顺序
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/07-pipe-compose.ts
思路说明:
- pipe: 从左到右,类似 Unix 管道
- compose: 从右到左,类似数学函数组合
- 使用 reduce 实现函数链式调用
关键代码:
typescript
function pipe<T>(...fns: Array<(arg: any) => any>) {
return (initialValue: T) => {
console.log('[PIPE] 初始值:', initialValue);
return fns.reduce((acc, fn, index) => {
console.log(`[PIPE] 步骤 ${index + 1}/${fns.length}: ${fn.name || '匿名函数'}`);
const result = fn(acc);
console.log(` 结果:`, result);
return result;
}, initialValue);
};
}
function compose<T>(...fns: Array<(arg: any) => any>) {
return (initialValue: T) => {
console.log('[COMPOSE] 初始值:', initialValue);
return fns.reduceRight((acc, fn, index) => {
console.log(`[COMPOSE] 步骤 ${fns.length - index}/${fns.length}: ${fn.name || '匿名函数'}`);
const result = fn(acc);
console.log(` 结果:`, result);
return result;
}, initialValue);
};
}
// 示例:数据转换管道
const trim = (s: string) => s.trim();
const toLowerCase = (s: string) => s.toLowerCase();
const words = (s: string) => s.split(' ');
const count = (arr: string[]) => arr.length;
const countWords = pipe(
trim,
toLowerCase,
words,
count
);
console.log(countWords(' Hello World ')); // 2</details>
练习 7.2: 实现柯里化和偏应用(进阶)
难度: ⭐⭐⭐⭐ 涉及知识点: 柯里化、偏应用、闭包、类型体操 预计时间: 35分钟
题目描述:
实现通用的柯里化函数和偏应用函数。
要求:
- 实现
curry(fn)自动柯里化函数 - 实现
partial(fn, ...args)偏应用 - 支持占位符
_用于跳过参数 - 保持类型安全
- 添加实用示例
提示: 柯里化返回单参数函数链,偏应用固定部分参数
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/07-curry-partial.ts
思路说明:
- 柯里化:将多参函数转为单参函数链
- 偏应用:预先固定部分参数
- 占位符:允许灵活的参数绑定
关键代码:
typescript
function curry<T extends (...args: any[]) => any>(fn: T) {
return function curried(...args: any[]): any {
console.log(`[CURRY] 收到 ${args.length}/${fn.length} 个参数`);
if (args.length >= fn.length) {
console.log('[CURRY] 参数已满,执行函数');
return fn(...args);
}
console.log('[CURRY] 参数不足,返回新函数');
return (...nextArgs: any[]) => {
console.log(`[CURRY] 追加 ${nextArgs.length} 个参数`);
return curried(...args, ...nextArgs);
};
};
}
const _ = Symbol('placeholder');
function partial<T extends (...args: any[]) => any>(
fn: T,
...presetArgs: any[]
) {
return (...laterArgs: any[]) => {
console.log('[PARTIAL] 合并参数');
const args: any[] = [];
let laterIndex = 0;
for (const arg of presetArgs) {
if (arg === _) {
args.push(laterArgs[laterIndex++]);
console.log(` 占位符 → ${args[args.length - 1]}`);
} else {
args.push(arg);
console.log(` 预设值 → ${arg}`);
}
}
args.push(...laterArgs.slice(laterIndex));
console.log('[PARTIAL] 最终参数:', args);
return fn(...args);
};
}
// 示例
const add = (a: number, b: number, c: number) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
const add5and = partial(add, 5, _);
console.log(add5and(10, 3)); // 18</details>
练习 7.3: 函数式错误处理(挑战)
难度: ⭐⭐⭐⭐⭐ 涉及知识点: Maybe/Option、Either、Railway Oriented Programming 预计时间: 45分钟
题目描述:
实现完整的函数式错误处理体系(Maybe 和 Either)。
要求:
- 实现
Maybe<T>类型(Some 和 None) - 实现
Either<L, R>类型(Left 和 Right) - 实现
map、flatMap、getOrElse等方法 - 实现链式调用的错误处理管道
- 提供实际应用示例
提示: Maybe 表示可能为空,Either 表示成功或失败
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/07-functional-error-handling.ts
关键代码 (详见 solutions 文件)
</details>
🆕 浏览器 API 练习
练习 8.1: Web Worker 图片处理(基础)
难度: ⭐⭐⭐ 涉及知识点: Web Worker、Transferable Objects、Canvas API 预计时间: 30分钟
题目描述:
使用 Web Worker 在后台线程中处理图片(灰度化)。
要求:
- 创建 Worker 处理图片数据
- 使用 Transferable Objects 优化传输
- 主线程显示处理进度
- 处理完成后更新 UI
- 处理多张图片时使用 Worker 池
提示: ImageData 可以转移所有权避免拷贝
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/08-web-worker-image.ts和solutions/08-image-worker.js
</details>
练习 8.2: IntersectionObserver 无限滚动(进阶)
难度: ⭐⭐⭐⭐ 涉及知识点: IntersectionObserver、虚拟滚动、性能优化 预计时间: 35分钟
题目描述:
实现一个高性能的无限滚动列表组件。
要求:
- 使用 IntersectionObserver 检测滚动到底部
- 分页加载数据(每次加载20条)
- 显示加载状态
- 实现虚拟滚动优化(只渲染可见项)
- 支持上拉加载更多
提示: IntersectionObserver 比 scroll 事件性能更好
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/08-infinite-scroll.ts
</details>
练习 8.3: 文件分片上传(挑战)
难度: ⭐⭐⭐⭐⭐ 涉及知识点: File API、Blob、Fetch API、并发控制 预计时间: 45分钟
题目描述:
实现支持断点续传的大文件分片上传。
要求:
- 将大文件切分为多个分片(每片5MB)
- 并发上传多个分片(最多3个并发)
- 显示上传进度
- 支持暂停和恢复
- 所有分片完成后合并文件
- 使用 AbortController 控制请求
提示: File.slice() 分片,Promise 并发控制
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/08-file-upload.ts
</details>
🆕 TypeScript 工程化练习
练习 9.1: 为无类型包编写声明文件(基础)
难度: ⭐⭐⭐ 涉及知识点: 声明文件、模块增强、DefinitelyTyped 预计时间: 25分钟
题目描述:
为一个没有类型定义的 npm 包编写 .d.ts 声明文件。
要求:
- 假设有一个JS库
my-logger,提供日志功能 - 编写完整的类型声明
- 支持默认导出和命名导出
- 包含函数重载
- 添加 JSDoc 注释
提示: 使用 declare module 声明模块
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/09-declaration-file.d.ts
</details>
练习 9.2: 模块增强与全局扩展(进阶)
难度: ⭐⭐⭐⭐ 涉及知识点: 模块增强、全局扩展、声明合并 预计时间: 35分钟
题目描述:
为第三方库添加自定义方法和属性。
要求:
- 扩展 Express 的
Request接口,添加user属性 - 扩展 Array 原型,添加
last()方法 - 扩展全局 Window 接口
- 确保类型安全
- 提供使用示例
提示: 使用 declare module 和 declare global
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/09-module-augmentation.ts
</details>
练习 9.3: 高级类型模式(进阶)
难度: ⭐⭐⭐⭐ 涉及知识点: 品牌类型、协变逆变、递归类型、infer 预计时间: 40分钟
题目描述:
实现一组高级类型工具。
要求:
- 实现品牌类型
Brand<T, Tag> - 实现
DeepPartial<T>递归可选 - 实现
PromiseValue<T>提取 Promise 值类型 - 实现
UnionToIntersection<U>联合转交叉 - 提供实际应用场景
提示: 使用条件类型和 infer 关键字
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/09-advanced-type-patterns.ts
</details>
练习 9.4: Monorepo 配置与项目引用(挑战)
难度: ⭐⭐⭐⭐⭐ 涉及知识点: Project References、Monorepo、tsconfig 继承 预计时间: 50分钟
题目描述:
配置一个 TypeScript Monorepo 项目,包含多个包和共享配置。
要求:
- 创建项目结构:packages/core、packages/ui、packages/utils
- 配置根 tsconfig.json 和各包的 tsconfig.json
- 设置 Project References
- 配置路径映射(@myapp/core 等)
- 实现包之间的类型共享
- 配置增量编译
提示: 使用 references 和 composite 选项
<details> <summary>💡 参考答案</summary>
完整代码见
solutions/09-monorepo-setup/
</details>
下一步
下一步: 实时聊天应用项目 和 ts-toolkit 工具库
通过完整项目巩固所学知识!💪