Matt Pocock Skills 是一套可验证、可落地的 TypeScript 类型能力训练路径,通过真实需求驱动泛型约束、条件类型推导与 infer 实践,配套本地环境搭建、infer 返回值三步解法、绕过分配律技巧、satisfies 替代方案及类型调试三步法定位问题。
☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 多模态理解力帮你轻松跨越从0到1的创作门槛☜☜☜

你想在 TypeScript 项目里真正用上泛型约束、条件类型推导、infer 捕获返回值,而不是每次写完都得加 // @ts-ignore 或退化成 any —— 这不是语法记不住的问题,是缺乏一套可验证、可落地的类型能力训练路径。Matt Pocock Skills 正是为此而生:它不教你怎么读文档,而是让你在一个个真实需求中亲手把类型契约写出来、跑通测试、看到红色变绿色。
初始化 Skills 本地练习环境
打开终端,进入你准备练习的空文件夹,执行:pnpm create @mattgpocock/skills@latest(推荐 pnpm;若用 npm,请确保 Node.js ≥ 18.17)。
安装过程会自动检测当前目录是否为空,【非空目录将直接退出,不会覆盖任何现有文件】。确认后,它会生成 .skills/ 目录、tsconfig.json 补丁、以及一个带预置测试的 starter.ts。
运行 pnpm dev 启动类型检查监听器,保存任意 .ts 文件时,终端会实时输出类型校验结果——这不是普通 tsc --noEmit,而是 Skills 自定义的诊断引擎,能识别出“本该 infer 出 string 却推导为 unknown”这类隐性失败。
从第一个技能开始:Infer 函数返回类型
打开 ./skills/01-infer-return-type/starter.ts,你会看到一个未完成的类型工具:
type InferReturn<t> = ???;</t>
方法一:用 infer 直接解包 Promise
写成 type InferReturn<t> = T extends Promise<infer r> ? R : T;</infer></t>。这能处理 Promise<string></string> → string,但对同步函数无效。
方法二:递归捕获所有调用链路
补全为 type InferReturn<t> = T extends (...args: any[]) => infer R ? R : T extends Promise<infer r> ? R : T;</infer></t>。注意这里必须把函数分支放在 Promise 分支前,否则联合类型会坍缩——【顺序错,整个类型就变成 never】。
方法三:用 satisfies + const 断言辅助验证
在测试用例里加一行 const test = (() => 'done') satisfies () => string;,再用 InferReturn<typeof test></typeof> 校验。这步不是必须,但它能帮你确认 infer 是否在真实调用上下文中生效,而非仅在 Playground 里“看起来对”。
绕过 distributive conditional types 的坑
第一步:识别触发场景
当你对联合类型如 string | number 做 T extends string ? A : B 判断时,TypeScript 默认启用分配律,把判断拆成 string extends string ? A : B 和 number extends string ? A : B 两路求值——这常导致意外的交叉类型或 never。
Python基础语法库,覆盖30+核心主题:数据类型、字符串操作、运算符、列表/元组/字典/集合、条件语句与循环、函数定义与高级函数(lambda/map/filter/reduce)、装饰器、生成器与迭代器、面向对象编程(类/继承/多态/魔术方法)、模块与包管理、文件IO、异常处理、常用内置函数与标准库(os/sys/json/datetime/collections)、类型提示、推导式。适用场景:Python语法查询、数据类型操作、函数编写、面向对象设计、模块使用、文件读写、异常捕获、装饰器编写、生成
第二步:用方括号包裹类型参数
改成 [T] extends [string] ? A : B。方括号让联合类型失去“可分配性”,强制整体匹配。这是 Matt 在 #17 事件总线练习里反复强调的技巧,也是他所有高级类型封装的底层开关。
第三步:验证是否生效
写一个测试:输入 type Result = DistributeCheck,期望输出 'a' | 'b' 而非 never。如果仍是 never,说明你漏掉了方括号,或者在某个嵌套层级又触发了隐式分配。
用 satisfies 替代 as const 的三个时机
当你要锁定字面量类型,但对象有深层嵌套结构时,as const 会过度收窄——比如 { user: { name: 'Alice' } } as const 会让 user 变成 { readonly name: 'Alice' },后续无法 assign 新用户。
方法一:只对最外层用 satisfies
const config = { api: 'https://x', timeout: 5000 } satisfies { api: string; timeout: number };。这既保留字段类型,又阻止多余属性,且不冻结可变性。
方法二:配合泛型约束做动态校验
写一个函数 function makeConfig<t extends record any>>(c: T) { return c satisfies T; }</t>,传入时 IDE 会实时标红非法字段,比运行时 Zod 校验快一个数量级。
方法三:在类型守卫中替代 type assertion
不用 if (typeof x === 'string') { (x as string).trim() },改用 if (x satisfies string) { x.trim() }——这行不通,因为 satisfies 不能用于运行时判断;正确写法是 if (typeof x === 'string') { const _ = x satisfies string; x.trim(); },用无副作用赋值触发编译期校验。
调试类型推导失败的三步定位法
打开 VS Code,把光标停在报错的类型变量上,按 Ctrl+Shift+P → 输入 “Go to Type Definition”。不要点进 node_modules,直接看 Skills 仓库里对应 .d.ts 的实现。
观察该类型是否依赖外部声明——比如用了 import type { SomeType } from 'external-lib'。如果是,立刻检查 node_modules/external-lib/index.d.ts 是否导出了完整类型,还是只导出了 declare module 'external-lib' 这种空壳。
删掉当前文件所有非必要 import 和注释,只留最小复现场景,再运行 pnpm tsc --showConfig。输出的 compilerOptions 里重点看 "skipLibCheck": false 和 "strict": true 是否生效——【这两项任一关闭,Skills 的大部分 infer 都会静默失效】。














