TypeScript 类型挑战实战:手写 Parameterslt;Tgt;,从函数类型提取参数元组(type-challenges 3312)
发布时间:2026/10/2 2:15:42 作者:尧图编辑部 阅读量:1,286
)
示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载本篇文章围绕 type-challenges 仓库中编号 3312 的初级easy挑战展开核心任务是在不使用内置ParametersT工具类型的前提下实现一个能从函数类型T中提取参数列表的MyParametersT。通过本文你将掌握infer在条件类型中的匹配机制、元组tuple类型与函数参数的对应关系以及如何借助仓库自带的测试框架验证自定义工具类型的正确性为后续挑战infer系列如 Get Return Type打下坚实基础。题目回顾复刻内置工具类型 Parameters题目原文见 questions/03312-easy-parameters/README.ja.md要求在不使用内置的类型工具ParametersT的情况下实现一个从T构建元组类型的类型。仓库中的英文版 README.md 表述为 Implement the built-inParametersTgeneric without using it中文版 README.zh-CN.md 则明确要求参考 TypeScript 官方工具类型文档中关于ParametersType的语义进行实现。题目给出了一个非常直观的示例const foo (arg1: string, arg2: number): void {} type FunctionParamsType MyParameterstypeof foo // [arg1: string, arg2: number]也就是说给定一个函数foo我们希望得到一个新的类型[arg1: string, arg2: number]——这是一个元组类型tuple type它按声明顺序列出了该函数的全部形参类型。如果函数没有参数期望的结果是空元组[]。从 info.yml 可以确认该题目的元数据难度easy初级标签infer、tuple、built-in作者midorizemi这三个标签精确概括了本挑战的三个核心知识点利用infer做类型推断、输出类型是元组、以及目标是复刻内置工具类型。从模板看题目设计意图每个挑战都提供一个待补全的模板文件。本挑战的 template.ts 内容如下type MyParametersT extends (...args: any[]) any any这个模板给出了两个关键信息泛型约束constraintT extends (...args: any[]) any表明T必须是一个函数类型。任何非函数类型传入时都会触发类型错误这一点与内置ParametersT的行为一致——内置工具类型同样只接受函数类型。待实现部分 any是占位符需要我们把any替换为真正的实现逻辑。函数类型在 TypeScript 中可以写为(...args: 参数类型列表) 返回值类型的形式其中参数列表部分恰好是一个元组。因此本挑战的实质是把T这个函数类型拆开单独取出它的参数元组部分。前置知识条件类型与 infer 的匹配机制要手写ParametersT必须先理解 TypeScript 类型系统里最强大的两个特性。条件类型Conditional Types条件类型的语法形如T extends U ? X : Y当类型T可以赋值给类型U时取X否则取Y。它是 TypeScript 类型层级的运行时分支是几乎所有工具类型实现的基础。infer在匹配中声明一个待推断变量infer关键字只能出现在条件类型的extends子句中。它的作用是在类型匹配的过程中把某个位置上的类型捕获到一个新声明的类型变量里供true分支使用。例如type GetReturnT T extends (...args: any[]) infer R ? R : never这里infer R会捕获函数T的返回值类型。同理如果想捕获函数参数type GetParamsT T extends (...args: infer P) any ? P : neverinfer P捕获的是(...args: ...)括号内的完整参数列表——由于函数参数列表的形态就是元组所以P的推断结果天然是一个元组类型。infer有两个使用要点它必须在条件类型extends的右侧出现且通常结合结构化匹配使用它只能出现在true分支中生效false分支需要返回一个兜底类型惯例是never。本挑战的最小可行解法综合以上两点最直接的解法是type MyParametersT extends (...args: any[]) any T extends (...args: infer P) any ? P : never逐段解读T extends (...args: any[]) any约束T必须是函数T extends (...args: infer P) any尝试把T匹配到参数列表未知、返回值任意的函数形态并把参数列表捕获为P? P匹配成功时返回捕获到的参数元组: never理论上不会走到因为约束已经保证了T是函数但类型系统仍要求给出兜底分支。回到题目示例验证MyParameterstypeof foo中T是(arg1: string, arg2: number) void匹配成功后P即为[string, number]带参数名的写法[arg1: string, arg2: number]与[string, number]在结构上是等价的元组。测试用例仓库如何验证你的实现每个挑战都配有 test-cases.ts它就是这个类型挑战的在线评测器import type { Equal, Expect } from type-challenges/utils function foo(arg1: string, arg2: number): void {} function bar(arg1: boolean, arg2: { a: A }): void {} function baz(): void {} type cases [ ExpectEqualMyParameterstypeof foo, [string, number], ExpectEqualMyParameterstypeof bar, [boolean, { a: A }], ExpectEqualMyParameterstypeof baz, [], ]三个用例分别覆盖了三种典型场景用例输入函数期望结果覆盖点1foo(arg1: string, arg2: number): void[string, number]多参数、基础类型2bar(arg1: boolean, arg2: { a: A }): void[boolean, { a: A }]对象字面量类型作为参数3baz(): void[]无参数函数应得到空元组其中Equal与Expect来自仓库根目录的 utils/index.d.ts。EqualX, Y使用经典的函数签名协变技巧实现严格相等比较export type EqualX, Y (T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2) ? true : falseExpectT extends true T则利用类型不满足true约束即报错的机制让任何不相等的情况在编译期直接变成红色错误。因此只要MyParameters的实现与内置Parameters的语义有任何偏差编译即失败——这套测试足以严格约束你的实现必须返回精确匹配的元组类型。深入原理为什么infer能推断出元组理解本挑战的难点在于函数参数列表在类型层面到底是什么在 TypeScript 中函数类型(arg1: string, arg2: number) void的参数部分可以等价写作(...args: [string, number]) void。也就是说函数的形参声明本身就是元组的语法糖。当T extends (...args: infer P) any执行匹配时TypeScript 编译器会把T参数位置的完整元组结构与(...args: infer P)对齐从而让P精确地绑定到该元组。这也解释了为什么内置ParametersT的官方语义README.zh-CN.md 指引参考的 TypeScript 工具类型文档会规定其返回函数类型的参数类型组成的元组。本质上内置实现的思路与本解法完全一致// 内置 Parameters 的典型实现思路等价形态 type ParametersT extends (...args: any) any T extends (...args: infer P) any ? P : never你在这个挑战中手写的正是 TypeScript 编译器内置同名工具类型的底层逻辑。扩展思考把同一个模式用到更复杂的场景掌握了用infer匹配函数类型并捕获参数/返回值这一模式后可以立即迁移到仓库中的其他挑战Get Return Type00002-medium-return-type题目要求实现内置ReturnTypeT解法只需把infer的位置从参数移到返回值上即T extends (...args: any[]) infer R ? R : never。该题从 test-cases.ts 可以看到测试覆盖了字面量返回值、对象类型、函数返回值、联合类型返回值等多种形态。Promise/Awaited 类挑战infer同样可以嵌套使用逐层剥离PromiseT的外壳。元组处理类挑战infer与元组的infer First、infer Rest配合可以递归地拆分数组类型实现First、Last、Pop等工具。可以自行验证的边界场景以下场景可作为额外练习自行验证不属于仓库既有测试用例供深化理解剩余参数rest parameterMyParameters(...args: number[]) void会推断为number[]因为剩余参数本身就是展开的元组可选参数(a?: string) void会推断为[a?: string] | undefined相关形态说明infer会忠实保留可选性标记重载函数overload直接对重载后的函数签名做推断结果取决于编译器选取的签名这也是Parameters在实际使用中需要注意的已知行为不建议依赖未验证的重载推断顺序。小结Parameters#3312是 type-challenges 中infer主题最基础的一道入门题它的价值在于一个模式贯通多种内置工具类型(...args: infer P) any捕获参数(...args: any[]) infer R捕获返回值掌握其一即可触类旁通元组是函数参数的载体理解函数参数列表即元组这一等价关系是后续处理元组、递归拆分参数列表的前提测试驱动类型开发借助 utils/index.d.ts 中Equal/Expect的组合把对类型实现的验证变成可编译、可复现的断言这也是该仓库所有挑战共用的验证范式。建议按顺序练习仓库中的infer系列题目先完成本题 Parameters再挑战 Get Return Type进而尝试Promise、Tuple to Union等进阶题目逐步建立对 TypeScript 类型系统的系统化理解。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐深入 type-challenges 第 3312 题用 infer 从零实现 Parameters\T\提取函数参数类型深入 type challenges 第 3312 题用 infer 从零实现 Parameters\T\ 提取函数参数类型 type challenge示例工程type-challenges 第 14 题实现 Firstlt;Tgt; 泛型提取元组首元素类型type challenges 第 14 题实现 Firstlt;Tgt; 泛型提取元组首元素类型 本篇指南基于 type challenges 仓库中示例工程type-challenges 第 3312 题精解不使用内置泛型实现 Parameterslt;Tgt;type challenges 第 3312 题精解不使用内置泛型实现 Parameterslt;Tgt; 本文聚焦 type challenges 仓库示例工程上一篇TranslucentTB让Windows任务栏焕然一新的轻量级美化神器下一篇VoiceCraft 完整指南几分钟跑通语音编辑与零样本 TTS新手也能出成品创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考