ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

在 type-challenges 中实现 MyReturnType:用 infer 条件类型提取函数返回类型

2026/9/30 8:13:34 拓冰建站 浏览量
在 type-challenges 中实现 MyReturnType:用 infer 条件类型提取函数返回类型 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载本篇技术指南以 type-challenges 仓库的第 00002 号中等难度题目「获取函数返回类型Get Return Type」为主线完整讲解如何在不使用内置ReturnTypeT的前提下借助条件类型与infer关键字自行实现返回类型提取工具。读完本文你将掌握infer在类型推断中的核心用法、函数类型解构的基本套路并学会如何对照仓库中的测试用例test-cases验证自己的类型实现。一、题目解读什么是 Get Return Type本题出自 questions/00002-medium-return-type/README.zh-CN.md作者是 Anthony Fu标签为#infer与#built-in难度为中等medium。题面要求非常明确不使用ReturnType实现 TypeScript 的ReturnTypeT泛型。也就是说我们要基于 TypeScript 内置ReturnType的行为规范从零实现一个同功能的类型工具MyReturnTypeT其输入是一个函数类型输出是该函数的返回值类型。题目给出的参考示例const fn (v: boolean) { if (v) return 1 else return 2 } type a MyReturnTypetypeof fn // 应推导出 1 | 2这里的fn是一个接受布尔参数、可能返回1或2的函数因此typeof fn的函数返回类型是联合类型1 | 2。MyReturnTypetypeof fn必须同样推导出1 | 2README 中用字符串形式的1 | 2描述期望结果。题目被打上#built-in标签是因为它要求我们重新发明一个 TypeScript 标准库中已经存在的工具类型而#infer标签则直接点明了核心解题武器——条件类型中的infer关键字。二、从模板开始理解起点代码每个 type-challenges 题目都在其目录下提供template.ts这是挑战者的起点。本题的模板位于 questions/00002-medium-return-type/template.ts内容极简type MyReturnTypeT any也就是说初始状态下MyReturnTypeT对任何类型都返回any类型检查会因与期望类型不符而失败。你的任务就是把any替换为真正能从T中提取返回类型的类型运算。需要注意的是模板中并没有约束T必须是函数类型。这意味着我们的实现要足够健壮要么通过约束T extends (...args: any[]) any将输入限定为函数要么借助条件类型在非函数输入时落入never分支仓库其余题目的实现风格也普遍采用这种先判断后提取的写法。三、核心原理条件类型与 infer3.1 条件类型Conditional Types条件类型的语法形如type IsStringT T extends string ? true : false当T可以赋值给string时结果为true否则为false。条件类型的分支中不仅可以使用字面量类型作为判断目标也可以使用带占位符的抽象模式来捕获子类型——这正是infer登场的地方。3.2 infer在条件类型中声明推断变量infer允许我们在extends的右侧声明一个类型变量TypeScript 编译器会根据实际传入的类型自动推断出该变量的具体值。最经典的例子是从数组类型中提取元素类型type ElementTypeT T extends (infer U)[] ? U : never当T是string[]时infer U被推断为string当T是number[]时U为number。函数类型同样可以解构。函数类型可以写作(...args: any[]) R的形式因此我们可以用type MyReturnTypeT T extends (...args: any[]) infer R ? R : never来提取函数返回类型。infer R位于函数类型的返回值位置编译器会把实际函数的返回类型赋给R。3.3 完整解答将上述思路写入模板得到本题的完整实现type MyReturnTypeT T extends (...args: any[]) infer R ? R : never对于题目的示例const fn (v: boolean) v ? 1 : 2 type a MyReturnTypetypeof fn // 1 | 2typeof fn是(v: boolean) 1 | 2infer R被推断为1 | 2因此a正确得到联合类型1 | 2。把答案写回 questions/00002-medium-return-type/template.ts即可替换原来的any。四、测试用例题目如何验证你的答案4.1 测试文件结构本题的验证逻辑位于 questions/00002-medium-return-type/test-cases.ts。文件首先导入了仓库统一的类型测试工具import type { Equal, Expect } from type-challenges/utils这两个工具定义在 utils/index.d.ts 中ExpectT extends true要求传入的泛型参数必须为true否则类型检查直接报错EqualX, Y通过一个巧妙的函数类型比对技巧(T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2)严格判断两个类型是否完全相等可区分any、联合类型、字面量类型等细微差别。测试的原理是Equal期望类型, MyReturnType输入函数为true再交给Expect校验。任何一处推断错误例如返回any、漏掉联合分支都会导致类型检查失败。4.2 逐条解读测试用例type cases [ ExpectEqualstring, MyReturnType() string, ExpectEqual123, MyReturnType() 123, ExpectEqualComplexObject, MyReturnType() ComplexObject, ExpectEqualPromiseboolean, MyReturnType() Promiseboolean, ExpectEqual() foo, MyReturnType() () foo, ExpectEqual1 | 2, MyReturnTypetypeof fn, ExpectEqual1 | 2, MyReturnTypetypeof fn1, ]逐条分析这些用例覆盖了返回类型提取的关键场景普通类型() string应提取出string() 123应提取出字面量类型123复杂对象() ComplexObject应提取出ComplexObject其中type ComplexObject { a: [12, foo] bar: hello prev(): number }验证了提取器对嵌套数组[12, foo]、字符串字面量hello、方法签名prev(): number等复杂结构类型依然原样返回 3.泛型类型() Promiseboolean提取出Promiseboolean说明infer R捕获的是完整返回类型本身不做任何拆解 4.返回函数类型() () foo提取出() foo即返回类型可以是函数类型这也暗示了链式提取的可能性——若想提取嵌套函数的最终返回类型需要递归地再次对R应用MyReturnType 5.联合类型返回typeof fn与typeof fn1均应提取出1 | 2其中const fn (v: boolean) v ? 1 : 2 const fn1 (v: boolean, w: any) v ? 1 : 2fn1额外带有一个any参数用于确认参数的数量与类型不会影响返回类型提取。4.3 测试如何被执行type-challenges 的题目通过tsc类型检查来判定对错只要test-cases.ts中ExpectEqual...的所有断言都通过即视为挑战成功。你可以在本地使用仓库根目录的 package.json 中锁定的 TypeScript^5.3.3对该文件做纯类型检查tsc --noEmit来验证你的template.ts实现。五、实现细节与边界讨论5.1 为什么用(...args: any[])而非(...args: unknown[])参数类型写成any[]是为了让任何函数都能匹配extends左侧的模式。若改用unknown[]某些带有特定参数要求的函数在严格模式下可能无法通过可赋值性判断导致落入never分支。这也是内置ReturnType与绝大多数社区实现采用的通用写法。5.2 非函数输入的行为由于条件类型具备未命中则走 false 分支的特性我们的实现T extends (...args: any[]) infer R ? R : never在T不是函数时例如MyReturnTypestring会得到never这是一种合理且可预期的降级行为。若希望强制约束也可写作type MyReturnTypeT extends (...args: any[]) any T extends (...args: any[]) infer R ? R : never但这会牺牲对非函数类型传入时的容错性两种风格各有取舍。5.3 infer 的其他应用线索本题标签中的#infer提示我们infer的能力远不止提取返回类型。同样的模式可以用于提取函数参数列表等价于内置ParametersT、提取Promise内部类型等价于内置AwaitedT的雏形、从模板字符串类型中提取字面量子串等。仓库中大量其他题目如00300-hard-string-to-number、00114-hard-camelcase都是对infer在字符串字面量层面应用的延伸完成本题是掌握这一核心技能的起点。六、小结本题的核心收获可以概括为一句话在条件类型的extends模式中使用infer R占位函数返回值位置即可让 TypeScript 编译器替你完成返回类型推断。对照 test-cases.ts 的七组断言MyReturnTypeT T extends (...args: any[]) infer R ? R : never这一行代码同时满足普通类型、字面量、复杂对象、泛型类型、函数返回值与联合类型六类场景这也是为什么它成为条件类型与infer学习路径中不可绕过的经典入门题。如果你想继续深入可以回到 README.zh-CN.md仓库根目录的官方中文总览查看完整题目列表或参考根目录 README.zh-CN.md 了解整个挑战项目的组织方式infer的进阶应用还可以结合仓库 guides/infer.md 的学习材料注当前仓库中该指南尚未完成以及utils工具库 utils/index.d.ts 中Equal、Expect等测试辅助类型的实现来加深理解。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐3步攻克Type Challenges函数返回类型提取3步攻克Type Challenges函数返回类型提取 你是否在TypeScript项目中遇到过需要提取函数返回类型的场景面对复杂的函数类型不知道如何准确获示例工程TypeScript 函数返回类型推断Type from Func Return从实现推导返回值类型TypeScript 函数返回类型推断Type from Func Return从实现推导返回值类型 导读本文聚焦 TypeScript 中根据函数实文档教程TypeScript 函数返回值类型推断从 The Concise TypeScript Book 的 Type from Func Return 到 ReturnType 与条件类型提取TypeScript 函数返回值类型推断从 The Concise TypeScript Book 的 Type from Func Return 到 R文档教程上一篇OneUptime 与 Slack 集成指南从通知配置到自托管网络架构下一篇IntelliJ Platform 性能实验 Jupyter Notebook 编写规范基于 uv 与 nb.py 构建可复现实验创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考