ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TypeScript 类型挑战 00017:用类型系统实现柯里化(Currying)的完整实战指南

TypeScript 类型挑战 00017:用类型系统实现柯里化(Currying)的完整实战指南 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载本篇技术指南围绕 type-challenges 仓库中编号 00017 的困难hard级挑战「柯里化 1」展开讲解如何用 TypeScript 类型系统为任意参数个数的函数推导出柯里化后的类型签名。读完本文你将掌握变长元组variadic tuple与递归条件类型在函数类型变换中的组合用法能够独立完成本挑战并为理解仓库中更高难度的「柯里化 2」动态参数柯里化挑战打下基础。挑战背景柯里化是什么柯里化Currying 是一种将带有多个参数的函数转换为一系列每个只带一个参数的函数的技术。例如一个普通的加法函数const add (a: number, b: number) a b const three add(1, 2)经过柯里化之后它的调用形态会从add(1, 2)变成逐次传参const curriedAdd Currying(add) const five curriedAdd(2)(3)本挑战编号 00017的难点在于传递给Currying的函数参数个数是不确定的你需要在类型层面写出一个通用的变换使得无论传入几个参数、什么类型都能得到对应的逐层柯里化函数类型。本挑战标注了「困难hard」难度并归入#array元组/数组主题分类文档同时建议在TypeScript 4.0下完成这是因为本解法依赖 TS 4.0 引入的「变长元组类型variadic tuple types」能力[...infer Rest]语法。挑战目标与类型规格原文档给出了本挑战的完整规格需要满足以下三点柯里化后的函数每次仅接受一个参数参数逐个被分配接受完所有参数后返回最终结果柯里化函数每一层的返回值类型必须精确推导不能退化成any。也就是说对于(a: string, b: number, c: boolean) true这样的三参数函数期望的类型是(a: string) (b: number) (c: boolean) true题目模板与测试用例解读模板文件本挑战的起点在 questions/00017-hard-currying-1/template.ts模板只有一个空实现declare function Currying(fn: any): any所有参数和返回值都被any吞掉了任务就是把它替换成能正确推导柯里化类型的签名。测试用例仓库为每个挑战都提供了类型级测试用例见 questions/00017-hard-currying-1/test-cases.ts。它通过Equal工具类型定义于 utils/index.d.ts逐一比对推导结果import type { Equal, Expect } from type-challenges/utils const curried1 Currying((a: string, b: number, c: boolean) true) const curried2 Currying((a: string, b: number, c: boolean, d: boolean, e: boolean, f: string, g: boolean) true) const curried3 Currying(() true) type cases [ ExpectEqual typeof curried1, (a: string) (b: number) (c: boolean) true , ExpectEqual typeof curried2, (a: string) (b: number) (c: boolean) (d: boolean) (e: boolean) (f: string) (g: boolean) true , ExpectEqualtypeof curried3, () true, ]从用例中可以提炼出本挑战必须覆盖的三个边界用例输入期望输出覆盖要点curried13 个参数(string, number, boolean)3 层柯里化链基本多参数场景curried27 个参数7 层柯里化链长参数列表与类型逐层对应curried30 个参数() true() true原样返回空参数边界注意curried3说明了一个重要细节当函数没有参数时柯里化结果就是它本身不能推导成错误的形态。解题思路四步构造柯里化类型第一步约束入参并提取参数与返回值首先把Currying的入参约束为函数类型并用infer拆出参数元组Args与返回值Rdeclare function CurryingT extends (...args: any[]) any(fn: T): CurriedT type CurriedF F extends (...args: infer Args) infer R ? /* 递归展开 Args */ : neverinfer Args得到的不是一个函数签名而是一个元组例如[string, number, boolean]这正是后续递归的原料。第二步处理空参数边界当Args是空元组[]时函数没有参数可分直接返回一个接受零参数并返回R的函数类型type CurriedF F extends (...args: infer Args) infer R ? Args extends [] ? () R : /* 继续递归 */ : never这一步对应测试用例中的curried3。第三步逐个拆出参数并递归生成下一层当Args非空时用变长元组语法拆出头元素A与剩余元组RestArgs extends [infer A, ...infer Rest] ? (arg: A) Curried(...args: Rest) R : never这里的关键是拆出第一个参数A作为当前层函数的入参然后把剩余参数 原返回值重新包成一个函数(...args: Rest) R继续交给Curried递归处理。每次递归吞掉一个参数直到Rest为空触发第二步的边界分支。第四步完整的参考解法将以上三步组合得到一份可提交的解法declare function CurryingT extends (...args: any[]) any(fn: T): CurriedT type CurriedF F extends (...args: infer Args) infer R ? Args extends [] ? () R : Args extends [infer A, ...infer Rest] ? (arg: A) Curried(...args: Rest) R : never : never用测试用例验证推导过程对(a: string, b: number, c: boolean) true第一轮拆出string得到(arg: string) Curried(b: number, c: boolean) true第二轮拆出number第三轮拆出boolean后Rest为空落入() R分支返回true最终展开为(a: string) (b: number) (c: boolean) true与curried1的期望完全一致对curried2的 7 参数函数递归会连续展开 7 层类型推导逐一对应对curried3的空参数函数Args extends []直接命中返回() true。知识点剖析这道题练的是什么变长元组类型Variadic Tuple Types[infer A, ...infer Rest]是 TS 4.0 的核心新语法它允许在元组中间/末尾展开剩余元素。没有它我们只能写[infer A, ...]这类无法捕获剩余部分的模式。这正是原文档建议「在 TypeScript 4.0 下完成」的原因——若使用更早版本...infer Rest无法编译通过。条件类型 infer 的递归Curried是典型的递归条件类型它以F extends (...args: infer Args) infer R为入口在真分支内继续构造新的函数类型并调用自身。递归的终止条件是Args extends []与运行时柯里化的「参数耗尽即返回结果」语义一一对应。与仓库相关挑战的关联仓库文档在本挑战底部列出了三条相关挑战可以作为练习链路14・第一个元素用infer A从元组中提取首元素是本挑战[infer A, ...infer Rest]拆解的前置热身16・排除最后一项对元组做「去尾」操作练习元组的拆解与重组462・柯里化 2地狱级进阶版要求柯里化函数每次可接受至少一个参数、数量不限对应bind这类动态传参场景在理解本挑战「每次一个参数」的递归骨架后可进一步挑战该题。验证与运行方式本仓库是类型挑战集合所有用例通过Equal进行编译期断言。完成template.ts的填写后可在本地运行 TypeScript 编译器仓库根目录 package.json 的 devDependencies 中配置了typescript ^5.3.3以tsc检查 questions/00017-hard-currying-1/test-cases.ts 是否存在类型错误npx tsc --noEmit questions/00017-hard-currying-1/test-cases.ts若Equal断言通过说明你的Curried类型推导与期望完全一致Equal的实现基于函数参数逆变比较比简单的extends互检更严格见 utils/index.d.ts。小结本挑战的核心价值在于训练「把运行时行为映射为递归类型变换」的思维柯里化把f(a, b, c)变成f(a)(b)(c)类型层面就对应把参数元组[A, B, C]递归展开为(arg: A) (arg: B) (arg: C) R。掌握了「infer 拆元组 递归终止分支 变长元组重组」这套组合拳你就拥有了应对绝大多数函数签名变换类挑战包括后续的柯里化 2的核心能力。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐TypeScript 柯里化类型挑战 Currying 100017-hard从零推导函数柯里化的完整类型签名TypeScript 柯里化类型挑战 Currying 100017 hard从零推导函数柯里化的完整类型签名 本篇文章围绕 type challenge示例工程TypeScript 柯里化实战用箭头函数链实现部分应用CurryingTypeScript 柯里化实战用箭头函数链实现部分应用Currying 柯里化Currying是将一个多参数函数拆解为一系列单参数函数的函数式编程技教程type-challenges 题解Hard 难度 Currying 1柯里化 1类型体操完整解析type challenges 题解Hard 难度 Currying 1柯里化 1类型体操完整解析 导读 Currying 1编号 17是 type示例工程上一篇开源项目Anchor CMS快速指南与常见问题解答下一篇Webgrind 开源项目常见问题解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表