泛型与类型运算
泛型不是给变量换成字母,而是把类型之间的关系写成可复用规则。输入与输出怎样对应、键与值如何配对、回调能处理多宽的输入、联合是逐项变换还是整体判断,才是类型运算要保留的信息。好的抽象使错误更靠近调用点,同时让实现能兑现契约。
核心模型
泛型不是给变量换成字母,而是把类型之间的关系写成可复用规则。输入与输出怎样对应、键与值如何配对、回调能处理多宽的输入、联合是逐项变换还是整体判断,才是类型运算要保留的信息。好的抽象使错误更靠近调用点,同时让实现能兑现契约。
原理与机制
泛型保留关系,约束只描述最低能力
identity<T>(x:T):T 把返回值与输入关联,any 则放弃这种信息。get<T,K extends keyof T>(obj:T,key:K):T[K] 让键只能来自对象,并让结果随具体键变化;keyof 是键集合,T[K] 是索引访问类型。T extends {length:number} 只保证可读 length,不赋予函数构造任意 T 的能力:调用者的 T 可能还要求 name。不能用 {length:0} 冒充 T;应返回明确的基础结构、原 T,或让调用者提供真正返回 T 的工厂。
satisfies 用于检查表达式契约
把路由对象直接注解为 Record<string,Route> 容易失去具体键集合;as 断言可能掩盖无法兑现的类型关系。satisfies 检查是否符合目标,同时保留表达式更具体的信息,但它仍可能参与上下文推断,不能说完全不影响推断。配置字面量可用 as const satisfies Record<"home"|"save",Route> 保留路径字面量并检查固定键是否齐全;Record<string,Route> 则不要求这两个键。额外属性检查与新鲜字面量有关,satisfies 不是通用 exact 类型,也不是运行时过滤。
回调可替换性要看消费与生产位置
如果 API 承诺传入任意 Animal,回调必须处理所有 Animal,只接受 Dog 的回调可能对 Cat 调用 bark,因此不安全。strictFunctionTypes 对函数参数的检查体现逆向兼容,而返回值作为生产位置通常可协变;方法语法有兼容性例外,不能用较宽松写法证明逻辑安全。类型参数同时被读写会受到双向约束。Dog[] 被当成可写 Animal[] 后混入 Cat 就是反例;readonly 视图阻止该视图写数组,却不冻结元素或其他可变别名。
条件类型先分清逐项与整体
T extends U ? X : Y 中,被检查的 T 是裸类型参数时,联合常逐成员计算并合并,适合过滤成员。用 [T] extends [U] 包装后则整体判断;这不是看到 extends 就一定遍历。Strings<T>=T extends string?T:never 将 string|number 变成 string,而整体是否全为 string 得 false。never 在分发中像空联合,Strings<never> 得 never;整体 [never] extends [string] 却成立,若业务要求空类型为 false,需要先独立判 never。
infer 匹配结构,不运行程序
infer 在条件类型匹配中引入待推断部分,例如 T extends readonly (infer U)[] ? U : never 提取数组或只读元组元素联合;提取函数返回类型也只是分析签名,不会执行函数或验证网络字段。复杂工具应覆盖联合、never、unknown、any 与只读数据等反例。类型参数只出现一次或层层递归却最后大量 as,常提示收益不足;过深的类型程序会增加检查耗时与错误理解成本。优先用直接联合、标准工具和有限映射表达业务关系。
最小示例
typescript预期编译器保留 homePath 的精确类型,并拒绝 missing;@ts-expect-error 应对应真实错误,删掉注释才能直接看到报错。A 和 B 展示逐项过滤与整体判断区别,它们在生成 JavaScript 后不存在。配置对象仍是普通运行时对象,as const 没有执行深冻结,代码也没有检验远端配置。
环境:TypeScript 4.9+;建议 tsc --strict --noEmit 检查。
核验:未执行教学示例;说明中的结果为机制推演,不是本轮测试通过记录。
边界与取舍
边界
- 满足泛型约束不等于能够构造所有可能的 T;as T 只是压住报错,不能修正契约。
- 只读视图不消除可变别名,strictFunctionTypes 的方法例外也不保证实际回调安全。
- satisfies、infer、条件类型均不执行运行时验证;any 与 never 等边界必须单独写类型测试。
取舍
- 精确泛型增强调用提示和错误定位,但无意义类型参数使 API 难读;保留真实关系即可。
- 固定键 Record 提供配置完整性,动态键更灵活;选择由是否有封闭键集合决定,不能只追求“类型最严格”。
口述示范
我会把泛型解释成输入输出关系,用 keyof 与索引访问保持键值对应,再说明约束不是任意 T 的构造许可。配置用 satisfies 做检查,回调按读写位置讨论兼容,条件类型明确分发、元组包装和 never 边界;所有这些都停留在编译期。
章节练习
为假设的 TranslateFlow 路由配置保留精确路径,同时禁止缺少 save 或多出未知路由。
展开检查点
- 用固定键 Record 与 as const satisfies;get 返回 T[K],避免把键拓宽成 string。
- 加入缺项、错方法、错误路径和未知键的编译失败用例,动态数据另行校验。
参考来源与核验边界
- TypeScript:Generics · 核验 2026-10-04
- TypeScript:Conditional Types · 核验 2026-10-04
- TypeScript 4.9:satisfies · 核验 2026-10-04
- TypeScript:strictFunctionTypes · 核验 2026-10-04
核验日期 2026-10-04。satisfies 需 TS 4.9+,建议 strict 与 noEmit 做正反例检查。本章 TypeScript 示例未执行编译;原题 J24 的历史验证状态继续保持 NOT_RUN,不把建议命令当通过记录。 本章新增示例未在本轮执行,不新增测试通过声明。
内容版本:2026-10-04.2 · 题目和来源 ID 保持原样,本站不将面经标签解释为企业官方出题或高频保证。