
1. 项目概述当AI助手开始“胡说八道”最近在TypeScript社区Matt Pocock这个名字和他的“Skill”概念火得不行。如果你经常和AI结对编程或者让Copilot、Claude帮你写代码大概率遇到过这种情况你让它写一个处理用户表单的函数它洋洋洒洒给你生成几十行乍一看类型定义齐全async/await用得飞起但仔细一读逻辑绕了三个弯还引入了两个根本用不到的第三方库。更气人的是它信誓旦旦地告诉你这段代码“高效且安全”。这就是典型的“AI瞎写”——它能生成语法正确的代码但代码的语义、设计模式和可维护性可能一塌糊涂最终在你的项目里堆起一座“屎山”。Matt Pocock作为一位顶级的TypeScript布道师和开发者体验专家他提出的“Skill”恰恰是针对这个痛点的一剂解药。这不仅仅是一个工具或插件更是一种方法论和一套最佳实践的集合。它的核心目标很明确教会AI如何像一位经验丰富的资深工程师一样思考和工作产出高质量、可维护的代码而不是仅仅生成一堆能运行的字符。简单来说Skill试图解决的是AI编程的“最后一公里”问题从“代码能跑”到“代码是好代码”的跨越。这对于任何使用GitHub Copilot、Cursor、Claude Code甚至本地大模型的开发者来说都是一个亟待解决的效率与质量瓶颈。如果你已经受够了在AI生成的复杂代码里debug或者厌倦了重构那些看似聪明实则脆弱的“AI作品”那么理解并应用Matt Pocock的Skill理念将会让你的开发体验提升一个维度。2. Skill核心思想拆解从“提示词工程”到“约束性框架”很多人把改善AI编码输出等同于优化提示词Prompt比如写得更详细、加入更多例子。这固然有用但Matt Pocock的Skill走得更远。它的本质是为AI编码行为建立一个强大的、可复用的“约束性框架”和“上下文环境”。2.1 为什么单纯的提示词不够用你可以尝试给AI一个非常详细的提示“用TypeScript写一个函数接收一个用户对象数组过滤出活跃用户然后返回他们的邮箱列表要求函数式编程使用filter和map做好错误处理。”AI可能会给你一个这样的版本interface User { id: number; name: string; email: string; isActive: boolean; } function getActiveUserEmails(users: User[]): string[] { try { if (!Array.isArray(users)) { throw new Error(Input must be an array of users); } const activeUsers users.filter((user: User) user.isActive true); const emails activeUsers.map((user: User) user.email); return emails.filter((email): email is string email ! null email ! undefined); } catch (error) { console.error(Error in getActiveUserEmails:, error); return []; } }这段代码能运行但问题很多类型断言(user: User)是多余的email is string的类型谓词用在这里有点杀鸡用牛刀错误处理过于笼统吞掉了异常并返回空数组可能掩盖了上游数据问题。这就像是AI记住了语法规则和常见模式但缺乏对代码“优雅度”和“语义清晰度”的判断。2.2 Skill的三大支柱Matt Pocock的Skill理念建立在三个核心支柱上它们共同构成了指导AI的“宪法”模式与惯例Patterns Conventions这不是指设计模式而是指团队或项目级别的编码惯例。例如命名是用fetchUser还是getUser事件处理器是用handleClick还是onClick错误处理是使用try/catch、返回Result对象、还是自定义错误类状态管理是用useState、Zustand还是TanStack Query异步处理是用async/await配合try/catch还是用.then().catch() Skill会明确定义这些惯例并强制AI在生成代码时遵守。比如一个Skill可以规定“在本项目中所有异步函数必须使用async/await语法错误必须使用try/catch块捕获并抛出自定义的AppError。”架构边界与约束Architectural Boundaries Constraints这是防止AI“越界”写代码的关键。Skill可以定义模块访问权限AI在编写components/Button.tsx时能否直接导入lib/database.ts很可能不行。Skill可以约束它只能导入utils/或types/下的特定工具。副作用管理在编写一个纯UI组件时Skill可以禁止AI生成直接调用fetch或修改localStorage的代码强制它通过Props接收数据或调用传入的回调函数。依赖注入规定某些服务如API客户端、日志工具必须通过上下文Context或参数传入而不是在函数内部直接实例化。质量与安全护栏Quality Safety Guardrails这是代码的“静态分析”层在AI生成代码的同时或之后立即进行检查。例如禁止使用any类型在TypeScript项目中这是一个铁律。循环复杂度限制生成的函数McCabe循环复杂度不能超过10。禁用特定的API或语法比如禁止使用eval()、禁止过时的var。安全检查对用户输入进行转义、避免SQL注入等模式的自动提示。通过这三大支柱Skill将模糊的“写出好代码”的期望转化为了AI可理解、可执行的具体、机械的规则。这比在每次对话中重复描述你的代码风格要高效和一致得多。3. 实操如何为你的项目定义并应用Skill理论听起来很棒但如何落地呢Matt Pocock倡导的是一种“渐进式”和“声明式”的Skill定义方法。下面我们以一个虚构的Next.js全栈项目为例看看如何一步步构建我们的Skill集。3.1 第一步识别痛点定义第一个Skill假设我们项目中最常见的问题是AI在组件里随意进行数据获取导致关注点分离混乱。我们可以定义一个名为no-direct-fetch-in-components的Skill。这个Skill的“源代码”通常可以是一个配置文件或一段有特定格式的提示词可能包含目标描述防止在React组件中直接进行数据获取逻辑鼓励使用服务层或自定义Hook。约束规则在/app/components/和/app/ui/目录下的.tsx或.ts文件中禁止使用原生的fetch、axios、useEffect内进行数据获取。允许导入来自/lib/api/或/hooks/useApi/的封装好的数据获取方法。正面示例// 好的做法使用自定义Hook import { useUser } from /hooks/useUser; function UserProfile() { const { user, isLoading } useUser(); // ... 渲染逻辑 }反面示例// 坏的做法在组件内直接fetch function UserProfile() { const [user, setUser] useState(null); useEffect(() { fetch(/api/user).then(r r.json()).then(setUser); // Skill会标记此行为 }, []); // ... 渲染逻辑 }强制执行这个Skill可以被配置到你的AI编码助手如Cursor的Agent模式中或者作为一个ESLint插件理论上在保存时检查。实操心得定义Skill时一定要从你最常Review、最头疼的代码问题开始。第一个Skill应该小而具体解决一个明确的痛点。成功应用后你和团队会立刻感受到价值从而有动力定义更多Skill。3.2 第二步建立项目级TypeScript规范Skill对于TypeScript项目一个强大的Skill是严格类型约束。我们可以定义一个strict-ts-conventionsSkill。其核心规则可能包括禁用any和隐式any要求所有类型必须显式定义。对于暂时无法确定类型的情况强制使用unknown并配合类型守卫。强制函数返回类型注解即使TS能推断也要求写明返回类型提升可读性。枚举Enum与字面量联合类型的选择规定优先使用字面量联合类型type Status idle | loading | success | error除非有特殊的反向映射需求才使用enum。接口Interface与类型别名Type Alias的选择规定优先使用interface定义对象形状因为它更适合扩展extends使用type进行联合、交叉或复杂类型运算。模块导入导出规范禁止使用export default为了更好的重构和代码导航强制使用命名导出。当AI在编写代码时这个Skill会在后台运行。如果AI试图生成一个返回any的函数Skill会介入并“纠正”它或者至少给出一个强烈的警告要求开发者确认。3.3 第三步利用工具链集成SkillMatt Pocock的理念正在被工具化。虽然目前还没有一个叫“Skill”的官方独立工具但其思想可以通过现有工具链实现AI助手内置规则像Cursor这样的编辑器其Agent模式允许你提供自定义的“系统提示”System Prompt。你可以将定义好的Skill文本作为系统提示的一部分长期指导AI的行为。这是目前最直接的集成方式。ESLint 自定义规则许多Skill规则可以转化为ESLint规则。你可以使用typescript-eslint插件并严格配置再辅以自定义规则例如禁止在组件目录直接导入axios在代码提交或保存时自动检查。Prettier 配置代码风格类的Skill如缩进、引号、行宽可以直接通过Prettier配置实现。Husky lint-staged在Git提交前通过Husky钩子运行ESLint和TypeScript编译器tsc --noEmit强制执行所有质量护栏防止不符合Skill的代码进入仓库。IDE插件/代码模板为常见的代码模式如创建一个新的API路由、一个React组件创建代码片段Snippet或文件模板。当AI需要生成这类代码时它可以直接调用模板确保结构符合Skill规范。一个关键的技巧是分层管理Skill将Skill分为“全局级”适用于所有项目如禁用any、“项目级”适用于当前技术栈如Next.js App Router规范和“目录级”适用于特定模块如/components/禁止副作用。这样管理起来更加清晰。4. 实战案例用Skill重构一段AI生成的“屎山”代码让我们看一个真实的例子。假设AI为我们的博客系统生成了一个文章列表组件// 原始AI生成代码 (问题代码) export default function PostList({ posts }: any) { const [filteredPosts, setFilteredPosts] useState(posts); const [search, setSearch] useState(); useEffect(() { const results posts.filter((post: any) post.title.toLowerCase().includes(search.toLowerCase()) ); setFilteredPosts(results); }, [search, posts]); const handleDelete async (id: number) { try { const response await fetch(/api/posts/${id}, { method: DELETE }); if (response.ok) { setFilteredPosts(prev prev.filter(p p.id ! id)); alert(Deleted!); // 直接使用alert } } catch (err) { console.error(err); alert(Delete failed!); } }; return ( div input typetext placeholderSearch... onChange{(e) setSearch(e.target.value)} / {filteredPosts.map((post: any) ( div key{post.id} h3{post.title}/h3 p{post.excerpt}/p button onClick{() handleDelete(post.id)}Delete/button /div ))} /div ); }这段代码的“臭味”包括使用any类型在组件内直接进行fetch操作使用alert进行用户反馈搜索逻辑放在useEffect中可以用useMemo优化。现在我们应用之前定义的几个Skillstrict-ts-conventions消除any定义明确的接口。no-direct-fetch-in-components将删除逻辑移至服务层。ui-feedback-convention规定必须使用Toast通知组件而非alert。在Skill的约束和指导下我们可以让AI或我们自己将其重构为// 应用Skill后重构的代码 import { useState, useMemo, useCallback } from react; import { toast } from sonner; // 使用定义的Toast组件 import { deletePost } from /lib/api/posts; // 从服务层导入 interface Post { id: number; title: string; excerpt: string; } interface PostListProps { posts: Post[]; } export function PostList({ posts }: PostListProps) { const [searchTerm, setSearchTerm] useState(); const [isDeleting, setIsDeleting] useStatenumber | null(null); // 使用useMemo优化搜索计算避免不必要的重渲染 const filteredPosts useMemo(() { return posts.filter(post post.title.toLowerCase().includes(searchTerm.toLowerCase()) ); }, [posts, searchTerm]); const handleDelete useCallback(async (postId: number) { if (!window.confirm(Are you sure?)) return; // 简单的确认可替换为自定义模态框 setIsDeleting(postId); try { await deletePost(postId); // 调用服务层函数 toast.success(Post deleted successfully.); // 状态管理应上移至父组件或通过SWR/mutation invalidate cache此处仅为示例 } catch (error) { console.error(Failed to delete post:, error); toast.error(Failed to delete post. Please try again.); } finally { setIsDeleting(null); } }, []); return ( div classNamespace-y-4 input typetext placeholderSearch posts... value{searchTerm} onChange{(e) setSearchTerm(e.target.value)} classNameborder p-2 rounded / div classNamespace-y-3 {filteredPosts.map((post) ( article key{post.id} classNameborder p-4 rounded shadow-sm h3 classNametext-xl font-semibold{post.title}/h3 p classNametext-gray-600 mt-2{post.excerpt}/p button onClick{() handleDelete(post.id)} disabled{isDeleting post.id} classNamemt-3 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 disabled:opacity-50 {isDeleting post.id ? Deleting... : Delete} /button /article ))} /div /div ); }可以看到重构后的代码类型安全、关注点分离、用户体验更好并且完全遵守了项目定义的Skill。AI在初始生成时如果就在这些约束下工作就能直接产出质量更高的代码。5. 常见问题与高级技巧在实际推行Skill理念的过程中你可能会遇到一些挑战。以下是一些常见问题的解决思路和进阶技巧。5.1 如何管理复杂的Skill集当Skill数量增多时管理会成为问题。建议采用以下策略版本化与文档化将Skill定义写成正式的文档如Markdown文件并放在项目根目录的.github/或.cursor/文件夹下。对Skill的修改要通过Pull Request进行就像管理代码一样。分类与标签给Skill打上标签如#typescript、#react、#architecture、#security。在AI助手的上下文中可以根据当前编辑的文件类型自动激活相关的Skill子集。优先级与冲突解决定义Skill的优先级。当两个Skill冲突时例如一个要求函数必须有返回类型另一个针对测试文件允许省略明确哪个Skill优先。5.2 Skill会限制AI的创造性吗这是一个很好的问题。Skill的初衷不是扼杀创造性而是将创造性引导到正确的地方。比如在业务逻辑的实现、算法优化、用户体验交互设计上我们鼓励AI天马行空。但在项目规范、架构边界、安全红线这些方面我们需要AI严格遵守纪律。这就像给优秀的建筑师AI一份清晰的建筑规范和安全标准他才能更高效、更安全地发挥其设计才华而不是把创造力浪费在纠结用哪种型号的螺丝上。5.3 对于小型或个人项目也需要Skill吗非常需要而且越早开始越好。个人项目往往是“屎山”的温床因为只有你自己容易放纵。在项目初期就定义几个简单的Skill如严格的TypeScript规则、固定的项目结构能极大地提升项目的长期可维护性。当你想把项目分享给别人或者几个月后回头修改时你会感谢当初制定了这些规则的自己。这相当于为未来的你铺设了一条平坦的道路。5.4 如何让团队接受并遵循Skill自上而下的命令往往效果不佳。可以尝试从痛点演示开始在团队会议上展示一两段由AI生成的、符合语法但设计糟糕的代码让大家感受其维护成本。然后展示应用Skill后的改进版本。共同制定组织一个简短的workshop让团队成员一起提出最让他们头疼的代码问题并共同将其转化为1-2个团队Skill。集体 ownership能提高遵守度。工具化降低门槛将最重要的Skill通过ESLint、Prettier等工具自动化。让人无需记忆工具自动检查。将Skill集成到CI/CD流水线中不符合规范的代码无法合并。循序渐进不要一次性推出20条Skill。先从最核心、共识度最高的1-3条开始让大家适应后再逐步增加。5.5 高级技巧上下文感知与动态Skill未来的Skill可能会更加智能。例如基于目录的Skill当AI在编辑backend/auth下的文件时自动激活“密码必须哈希存储”、“使用JWT标准库”等安全相关Skill在编辑frontend/components/ui时则激活“使用Tailwind CSS”、“禁止内联样式”等UI规范Skill。基于Git历史的Skill分析项目历史提交自动总结出团队常用的模式例如“我们总是用useCallback包裹事件处理器”并将其建议为新的Skill。学习与进化Skill系统可以记录开发者接受或拒绝AI建议的修正从而微调规则的严格程度或适用范围。Matt Pocock的Skill概念本质上是在人机协作编程的新范式下重新定义“代码规范”。它不再是贴在墙上的一纸规章而是内嵌到AI编码助手思维过程中的“肌肉记忆”。通过精心设计和应用Skill我们不是在与AI的“瞎写”作斗争而是在系统地培养一个符合我们最高标准的、不知疲倦的结对编程伙伴。这个过程需要投入但回报是长期且巨大的一个更清晰、更健壮、更易于演进的代码库。