尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

type-challenges 第 533 题 Concat 解析:在类型系统里实现 `Array.concat`

type-challenges 第 533 题 Concat 解析:在类型系统里实现 `Array.concat` 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载导读Concat 是 type-challenges 题库中编号 533 的 easy 级数组array主题挑战由 Andrey KrasovskyGitHub 账号 bre30kra69cs 中difficulty: easytags: array。本题要求把 JavaScript 内置的Array.concat方法移植到 TypeScript 的类型系统中给定两个数组/元组类型参数返回一个按「从左到右」顺序合并两者元素的新数组类型。读完本文你将掌握变长元组类型variadic tuple types与 spread 语法在类型层的核心用法并能够完整通过仓库自带的 5 组正向用例与 1 组反向用例。题目要求在类型系统中实现Array.concat需求描述原题README.ja.md的表述是在类型系统中实现 JavaScript 的Array.concat函数。这个类型接收两个参数返回一个按照输入参数顺序ltr order从左到右包含全部元素的新数组。与运行时[1, 2].concat([3, 4]) // [1, 2, 3, 4]的行为对应类型层面ConcatT, U的目标是把两个数组/元组类型拼接成一个新元组类型type Result Concat[1], [2]; // expected to be [1, 2]起点模板仓库中供解题者填写的起点文件是 template.ts只有一个占位实现type ConcatT, U any任务就是把any替换为真正的类型计算逻辑同时保持Concat接收两个泛型参数的形式不变。测试用例5 组正向 1 组反向解题是否通过由 test-cases.ts 决定。它从type-challenges/utils导入了Equal与Expect该工具包源码位于 utils/index.d.ts其中Equal基于著名的函数逆变比较技巧实现类型相等判定并声明了一个as const元组import type { Equal, Expect } from type-challenges/utils const tuple [1] as const type cases [ ExpectEqualConcat[], [], [], ExpectEqualConcat[], [1], [1], ExpectEqualConcattypeof tuple, typeof tuple, [1, 1], ExpectEqualConcat[1, 2], [3, 4], [1, 2, 3, 4], ExpectEqualConcat[1, 2, 3], [false, boolean, 4], [1, 2, 3, false, boolean, 4], ] // ts-expect-error type error Concatnull, undefined逐条拆解这些用例可以提炼出实现必须满足的约束用例输入期望输出考察点1Concat[], [][]两个空元组拼接仍是空元组2Concat[], [1][1]空元组 单元素元组3Concattypeof tuple, typeof tuple[1, 1]readonly元组参与拼接4Concat[1, 2], [3, 4][1, 2, 3, 4]多元素元组按 ltr 顺序合并5Concat[1, 2, 3], [false, boolean, 4][1, 2, 3, false, boolean, 4]混合字面量类型、原始类型与联合类型反向Concatnull, undefined编译报错ts-expect-error参数必须是数组/元组而非null、undefined注意用例 3const tuple [1] as const的推断类型是readonly [1]因此实现必须能接受readonly修饰的元组而反向用例要求Concatnull, undefined触发类型错误说明两个泛型参数上应存在数组/元组的约束extends限制这正是「约束参数」与「返回新类型」的双重考点。参考实现一行 spread 解决问题结合用例约束规范且能全部通过的实现如下type ConcatT extends readonly unknown[], U extends readonly unknown[] [...T, ...U]要点拆解T extends readonly unknown[]/U extends readonly unknown[]对两个参数施加元组约束。之所以写readonly unknown[]而不是unknown[]是因为用例 3 传入的是readonly [1]若约束写成可变数组类型readonly元组无法满足约束而报错。[...T, ...U]利用 TS 3.0 引入的变长元组类型variadic tuple types把T和U的已知元素按 spread 顺序展开进新元组天然保证 ltr 顺序。返回新数组spread 每次都会产生全新的元组类型绝不修改输入这与Array.concat返回新数组而非原地修改的语义一致。若把约束放宽为只要求数组而不带readonly则用例 3 无法通过若不写约束直接type ConcatT, U [...T, ...U]则反向用例Concatnull, undefined不会报错。所以约束这一行不是装饰而是通过测试的硬性前提。底层原理变长元组类型与 spread为什么能在类型里「展开」元组TypeScript 3.0 引入的变长元组类型允许在元组类型的任意位置使用rest 元素。[...T, ...U]中的 spread 会在编译期被求值当T、U都是具体元组时编译器会计算出展开后的完整元素序列得到一个可被Equal精确比较的新元组类型。与readonly的关系readonly [1]表示只读元组。在普通函数约束T extends unknown[]下只读数组不被视为可赋值给可变数组因此必须显式声明extends readonly unknown[]。这也是为什么测试里特意使用const tuple [1] as const来构造readonly [1]——本题把「只读元组也能拼接」作为一项显式能力要求。空元组的处理当T []或U []时spread 空元组不产生任何元素[...[], ...[1]]自然收敛为[1]因此用例 1、2 无需任何特判分支。同族挑战Push 与 Unshiftinfo.yml 的related字段标明了本题的两个关联挑战3057 Push 与 3060 Unshift。三个挑战共用同一条「spread 构建新元组」的心法只是插入位置不同Pushquestions/03057-easy-push把单个元素追加到末尾模板为 template.ts实现为type PushT, U [...T, U]。其 test-cases.ts 还额外用// ts-expect-error验证了Pushnumber[], string等错误用法必须报错说明它同样依赖参数约束。Unshiftquestions/03060-easy-unshift把单个元素插入头部实现为type UnshiftT, U [U, ...T]test-cases.ts 验证了Unshift[1, 2, 3], boolean得到[boolean, 1, 2, 3]。三者对比可归纳出规律「拼接」类类型操作的本质就是在结果元组的合适位置放置 spread...。Concat 是其中唯一需要展开两个元组参数的变体把它吃透后Push/Unshift 几乎可以「顺手解出」。仓库根目录的 README.zh-CN.md 与 TODOs.md 记录了完整的挑战列表与推进规划方便按难度递进刷题。本地验证方式仓库使用 pnpm 管理依赖见根目录 package.json 中的packageManager: pnpm8.12.1并提供了type-challenges/utils工作区包utils/package.json 显示其版本为 0.1.1类型定义见 utils/index.d.ts。验证某题是否通过可在安装依赖后对对应目录执行 TypeScript 类型检查例如pnpm install pnpm -C questions/00533-easy-concat exec tsc --noEmit test-cases.ts在支持 TypeScript 的编辑器如 VSCode中直接打开 test-cases.ts观察Expect行是否出现类型错误是更直观的即时反馈方式。当模板 template.ts 中的any被正确替换后5 组ExpectEqual...全部成立Concatnull, undefined处的ts-expect-error也正好被「按预期报错」消费测试即全部通过。常见误区与排查思路约束漏写readonlyT extends unknown[]会导致Concattypeof tuple, typeof tuple直接报「readonly 元组不可赋值给可变数组」用例 3 失败。约束不写反向用例Concatnull, undefined不会产生类型错误ts-expect-error反而会因「未发生预期错误」而报错。顺序写反写成[...U, ...T]会通过类型检查但输出顺序颠倒无法满足 ltr 语义用例 4、5 失败。误用concat之类运行时方法本题完全在类型层求解模板文件中不应出现任何 JS 运行时表达式Equal做的是结构相等比较只有类型层面真正产出[1, 2, 3, 4]这样的元组才能通过。综上Concat 虽被标记为 easy却是一次性覆盖了「泛型约束」「readonly 兼容」「变长元组 spread」「正向/反向用例验证」多个基础能力的优质入门题也是后续大量数组类 hard 挑战如 Chunk、Flatten 等的通用地基。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐type-challenges 第 533 题 Concat在类型系统里实现数组拼接type challenges 第 533 题 Concat在类型系统里实现数组拼接 导读 本指南围绕 type challenges 仓库中的第 533 道示例工程type-challenges 题解533 Concat —— 在类型系统中实现数组拼接type challenges 题解533 Concat —— 在类型系统中实现数组拼接 本文围绕 type challenges 仓库中编号 533 的 E示例工程type-challenges 第 3057 题 Push 全解在类型系统里实现 Array.push 的泛型版本type challenges 第 3057 题 Push 全解在类型系统里实现 Array.push 的泛型版本 本文聚焦 type challenges示例工程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表