ARTICLE DETAIL

资讯详情

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

Type Challenges 题解:Optional Keys——提取对象中所有可选属性的键

Type Challenges 题解:Optional Keys——提取对象中所有可选属性的键 示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载OptionalKeysT是 type-challenges 仓库中的一道 hard 难度、utils标签的进阶工具类型题题号 00090要求实现一个类型工具把类型T中所有可选属性optional property的键合并为一个联合类型union。本文以该题目标签 questions/00090-hard-optional-keys/README.md 为核心结合仓库内的 template.ts 与 test-cases.ts从题目要求、测试用例边界、两种核心解题思路到与兄弟题目RequiredKeys、GetReadonlyKeys的对比层层拆解读完后你不仅能独立完成本题还能掌握可选的映射类型修饰符-?、Pick联合可选性检测等一系列可复用的类型体操技法。题目概览需求与原始模板原题文档对任务的描述只有一句话Implement the advanced util typeOptionalKeysT, which picks all the optional keys into a union.即实现高级工具类型OptionalKeysT将T中所有可选属性的键合并为一个联合类型。例如{ a: number, b?: string }中b带可选标记?因此结果应为b。仓库为该题提供了初始模板 questions/00090-hard-optional-keys/template.ts内容是一个待填充的占位实现type OptionalKeysT any解题者的任务就是把any替换为真正的类型逻辑并通过 test-cases.ts 中的全部用例。在仓库的元信息文件 questions/00090-hard-optional-keys/info.yml 中可以看到该题被标记为difficulty: hard、tags: utils并且声明了两个关联题目related: 89, 5——即 00089 号RequiredKeys必需的键与 00005 号GetReadonlyKeys只读键。这两道题与本题同属按属性修饰符筛选键的工具类型家族后文会逐一对比。逐条拆解测试用例边界在哪里题目评判依赖仓库中的 test-cases.ts。它引入type-challenges/utils提供的Equal、Expect两个辅助类型定义见 utils/index.d.ts对OptionalKeys提出了 4 组要求import type { Equal, Expect } from type-challenges/utils type cases [ ExpectEqualOptionalKeys{ a: number, b?: string }, b, ExpectEqualOptionalKeys{ a: undefined, b?: undefined }, b, ExpectEqualOptionalKeys{ a: undefined, b?: undefined, c?: string, d?: null }, b | c | d, ExpectEqualOptionalKeys{}, never, ]逐个解读这些用例能提炼出实现必须满足的四条硬性约束基础场景{ a: number, b?: string }中只有b带?结果必须是b不能包含必填的a。可选值为undefined的场景{ a: undefined, b?: undefined }中a是必填但值为undefined的属性b才是可选属性。结果必须精确为b。这一条说明判断依据是键是否带可选标记而不是值的类型是不是undefined——a: undefined是必填属性绝不能因为其值类型恰好是undefined就被误判为可选。混合多可选键场景{ a: undefined, b?: undefined, c?: string, d?: null }的结果是b | c | d覆盖了可选值类型为undefined、string、null的多种情况再次印证判断必须基于键的修饰符本身。空对象场景OptionalKeys{}应为never。空类型没有任何键联合类型为空标准做法就是用never表达。其中第 2、4 条是最容易翻车的点只看值类型会误伤a: undefined不做空对象特判则可能输出keyof {}之外的空结构。解法一{} extends PickT, K——空对象可赋值判可选TypeScript 中有一个非常优雅的事实只有全部属性都可选的类型才能被空对象类型{}赋值。反过来只要存在任何必填属性{}就无法赋值给它。利用这一点可以写出最简洁、最经典的一种实现type OptionalKeysT keyof { [K in keyof T]-?: {} extends PickT, K ? K : never }分三层理解这个写法PickT, K内建工具类型从T中取出键K及其属性且保留属性的可选/只读修饰符。对{ b?: string }取b得到{ b?: string }可选标记不会丢失。条件判断{} extends PickT, K ? K : never逐个键判断空对象能否赋给该键的子类型。能赋值说明该键可选产出K否则产出never。这样所有必填键在结果里全部塌缩成never。映射类型修饰符-?遍历时把被检测属性拷贝为必填避免结果类型本身带着可选标记随后keyof取键时才能拿到干净的键联合。keyof作用于映射类型会把值为K或never的键重新收集为联合类型never在联合中自动被剔除。把它套回 4 组用例验证{ a: number, b?: string }{} extends { a: number }为假 →never{} extends { b?: string }为真 →b。keyof { a: never, b: b }的结果是never | b即b。✅{ a: undefined, b?: undefined }{} extends { a: undefined }为假a必填{} extends { b?: undefined }为真 →b。✅ 完美避开按值类型判断的陷阱。{ a: undefined, b?: undefined, c?: string, d?: null }→b | c | d。✅{}映射类型无任何键keyof得到never。✅这种写法无需引入任何自定义辅助类型只依赖内建Pick与映射类型语法是社区中最常见的参考答案之一。解法二EqualPickT, K, PartialPickT, K——化可选前后是否同一判可选另一种思路是把可选翻译成部分化前后类型相等对某个键K的PickT, K若把它Partial全部改为可选之后类型没有任何变化说明它本来就是可选的。利用仓库 utils/index.d.ts 中定义的严格相等判断Equal可以写成type OptionalKeysT { [K in keyof T]-?: EqualPickT, K, PartialPickT, K extends true ? K : never }[keyof T]原理同样是三步PickT, K取出带原始修饰符的子类型PartialPickT, K强制把所有属性改为可选用Equal严格比较二者。Equal的实现是经典的函数型同构检测见 utils/index.d.ts(T() T extends X ? 1 : 2) extends (T() T extends Y ? 1 : 2)能区分any、never等细微差别比extends单向检查更严格。若原键必填Partial后{ a: number }变成{ a?: number }二者不等 →never若原键可选Partial前后完全一致 → 产出K。最后通过索引访问[keyof T]把映射结果合并成联合。这种解法与解法一殊途同归但多演示了一个可复用的范式对子类型施加变换用Equal比较变换前后是否一致在判断只读、可选、必填等修饰符类问题上都通用。两种解法的共同核心可以总结为一句口诀逐个键做Pick-?抹掉可选标记条件分支留下K、丢弃never最后keyof收拢成联合。兄弟题目对比RequiredKeys 与 GetReadonlyKeysOptionalKeys并非孤例。仓库在 questions/00090-hard-optional-keys/info.yml 中显式标注了两个关联题理解它们之间的异同能把这套技法串成完整知识网。00089 · RequiredKeys必需的键见 questions/00089-hard-required-keys/template.ts 与 test-cases.ts。它是OptionalKeys的镜像提取所有必填属性的键。其用例与本题几乎一一对称ExpectEqualRequiredKeys{ a: number, b?: string }, a, // 与 OptionalKeys 的 b 互补 ExpectEqualRequiredKeys{ a: undefined, b?: undefined }, a, // a 必填值为 undefined 也要选中 ExpectEqualRequiredKeys{ a: undefined, b?: undefined, c: string, d: null }, a | c | d, ExpectEqualRequiredKeys{}, never,实现上只需把OptionalKeys的条件取反例如{} extends PickT, K ? never : K。两道题放在一起能直观看出键必填/可选是一枚硬币的两面判断手段完全相同。00005 · GetReadonlyKeys只读的键见 questions/00005-extreme-readonly-keys/template.ts 与 test-cases.ts。它被标为 extreme 难度任务是提取所有只读属性的键interface Todo1 { readonly title: string; description: string; completed: boolean } interface Todo2 { readonly title: string; readonly description: string; completed?: boolean }注意其用例中Todo2.completed是可选但非只读的属性说明判定维度是readonly修饰符而非?修饰符——与本题的判定维度正好互补。解法套路仍是逐键Pick 施加变换 Equal比较前后是否一致只是变换从Partial换成去掉只读可用-readonly映射修饰符完成。至此三题构成了一张完整的属性修饰符探针图谱题目难度提取目标判定维度Optional Keys (00090)hard可选属性的键?修饰符Required Keys (00089)hard必填属性的键无?修饰符Get Readonly Keys (00005)extreme只读属性的键readonly修饰符如何在仓库中验证你的实现本仓库是一套 TypeScript 类型挑战合集解题无需运行环境验证即类型编译通过。工作流程如下编辑 questions/00090-hard-optional-keys/template.ts将type OptionalKeysT any替换为你的实现保持 test-cases.ts 不变其中ExpectEqual...的机制是Equal返回布尔字面量ExpectT extends true要求结果必须精确为true见 utils/index.d.ts。若实现有误Equal得到falseExpect的约束失败编译器直接报错——报错即失败无报错即通过依次跑通全部 4 个用例后还可以把题面替换为RequiredKeysquestions/00089-hard-required-keys/test-cases.ts或GetReadonlyKeysquestions/00005-extreme-readonly-keys/test-cases.ts的用例做横向演练检验自己对条件取反、修饰符变换的理解是否到位。小结OptionalKeysT虽是一道 hard 题但其解法高度凝练用Pick保修饰符、用映射类型逐键扫描、用-?归一化、用条件分支筛键、用keyof聚合。掌握它之后可选键/必填键/只读键三兄弟00090 / 00089 / 00005可以一并拿下{} extends X与EqualX, Y这两件判断工具也能迁移到大量其他类型挑战中复用。赞分享示例工程【免费下载链接】type-challengesCollection of TypeScript type challenges with online judge项目地址https://gitcode.com/GitHub_Trending/ty/type-challenges点击查看免费下载相关推荐Type Challenges 进阶实现 GetRequired\T\从对象类型中提取所有必需属性Type Challenges 进阶实现 GetRequired\T\ 从对象类型中提取所有必需属性 导读 GetRequiredT 是 type ch示例工程SQLFluff 方言开发实战从解析原理到贡献一个方言语法修复SQLFluff 方言开发实战从解析原理到贡献一个方言语法修复 SQLFluff 是一个模块化的 SQL 解析器、Linter 与自动格式化工具其全部方言语示例工程FiftyOne Enterprise Auto Labeling 实战配置并启动零样本自动标注运行FiftyOne Enterprise Auto Labeling 实战配置并启动零样本自动标注运行 Auto Labeling 是 FiftyOne Ent示例工程上一篇V-Net PyTorch 实现项目推荐下一篇如何用LlamaParse实现表格识别从财务报表到技术文档的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表