
freeCodeCamp 每日编程挑战 Challenge 299用 Schema Validator 手写 TypeScript 风格的对象结构校验【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本篇以 freeCodeCamp 仓库中的每日编程挑战 Challenge 299: Schema Validator Part 5 为主体完整讲解该挑战给出的对象 Schema 语法联合类型、可选字段、字符串数组、允许额外键与全部 10 组测试用例并结合 challenge-types.ts 与 块配置文件 说明该挑战在课程体系中的类型定义与运行机制。读完后你能够独立实现一个无运行时类型系统环境下的结构校验器并理解 TypeScript 风格 Schema 描述在纯 JavaScript 中对应的逐项判断逻辑。挑战定位每日编程挑战中的 Schema Validator 系列挑战源文件位于 6a0dcc730cb92a616f86f0c3.mdfrontmatter 声明其challengeType: 28。从 challenge-types.ts 源码可以看到const dailyChallengeJs 28即该挑战属于JavaScript 每日编程挑战类型同文件中viewTypes[dailyChallengeJs]为classic经典单编辑器视图submitTypes[dailyChallengeJs]为tests以自动测试断言判定通过getIsDailyCodingChallenge()则将 28 与 29Python 版统一识别为每日挑战。从 块配置 可见该系列的运行时特征dashedName: daily-coding-challenges-javascripthelpCategory: JavaScriptusesMultifileEditor: true启用多文件编辑器含测试面板disableLoopProtectTests: true关闭循环保护测试允许挑战中的无限循环写法被测试超时机制而非循环保护机制捕获blockLayout: legacy-challenge-list按挑战列表布局展示。该挑战是 Schema Validator模式校验器六部曲的第五部完整系列在块配置中排布为 Challenge 295300对应文件分别为 Part 1、Part 2、Part 3、Part 4、本挑战 Part 5 与 Part 6。Part 5 的核心增量在于Schema 中同时出现联合类型pipe 运算符、可选字段?与数组类型string[]且明确允许额外键。挑战描述待实现的 Schema挑战给出的完整描述如下源自原挑战文件的--description--段落Given an object (JavaScript) or dictionary (Python), determine if it matches the following schema:Roles user | creator | moderator | staff | admin { username: string, posts: number, verified: boolean, role: Roles, supporter?: boolean, badges: string[] }四条语法规则逐条拆解管道符|表示或role的取值必须是Roles联合类型中列举的字面量之一即user、creator、moderator、staff、admin五个值之一其他任何值包括大小写不同的写法都不合法。问号?表示可选字段supporter可以不出现但一旦出现其值必须是boolean。注意这与出现时可为任意类型不同——可选只豁免缺失不豁免类型错误。方括号[]表示数组badges必须是字符串数组且空数组合法即(or empty)。数组长度不限但每个元素都必须通过typeof string检查。Extra keys are allowed允许额外键对象中可以携带 Schema 未声明的字段如followers: 1233不影响校验结果。这一点使该校验器属于宽松结构匹配等价于 TypeScript 结构类型检查中的多余属性不拒绝语义对运行时对象而言结构类型本就不排斥多余属性。种子代码与任务边界挑战给定的种子代码如下function isValidSchema(obj) { return obj; }任务边界非常明确函数接收一个普通对象返回一个真正的布尔值表示是否匹配上述 Schema。种子代码直接return obj因此任何不重写返回逻辑的提交都无法通过assert.isTrue/assert.isFalse断言。测试用例全解原挑战的--hints--段落提供了 10 组测试用例覆盖了每一种通过/失败模式。以下完整继承并逐一说明每组的验证意图应返回true的三组// 用例 1完整合法对象所有字段齐备 assert.isTrue(isValidSchema({ username: gill, posts: 12, verified: false, role: creator, supporter: false, badges: [ early-adopter, popular ] })); // 用例 2合法对象 额外键 followers验证 Extra keys are allowed assert.isTrue(isValidSchema({ username: tonya, posts: 299, verified: true, role: moderator, supporter: true, badges: [ streak-master, veteran ], followers: 1233 })); // 用例 3边界值——posts 为 0、badges 为空数组验证 0 是合法 number、空数组合法 assert.isTrue(isValidSchema({ username: zara, posts: 0, verified: false, role: user, supporter: false, badges: [] }));应返回false的七组每一组精确命中一条规则// 用例 4badges 混入数字 18违反 string[] 的元素类型约束 assert.isFalse(isValidSchema({ username: nicole, posts: 65, verified: true, role: admin, supporter: false, badges: [ first-post, 18 ] })); // 用例 5整体缺失必需的 badges 字段 assert.isFalse(isValidSchema({ username: tim, posts: 25, verified: true, role: staff, supporter: false })); // 用例 6可选字段 supporter 出现了但类型是字符串 no违反 出现则必须 boolean assert.isFalse(isValidSchema({ username: charlie, posts: 0, verified: false, role: user, supporter: no, badges: [ first-post, anniversary ] })); // 用例 7role 取值 friend 不在 Roles 联合类型内 assert.isFalse(isValidSchema({ username: wanda, posts: 15, verified: true, role: friend, supporter: true, badges: [ popular ] })); // 用例 8verified 是字符串 false 而非布尔值 false陷阱typeof false string assert.isFalse(isValidSchema({ username: guy, posts: 5, verified: false, role: staff, supporter: true, badges: [ helper ] })); // 用例 9缺失必需的 posts 字段 assert.isFalse(isValidSchema({ username: carrie, verified: true, role: moderator, supporter: true, badges: [ helper, sharer ] })); // 用例 10username 是布尔值 true 而非字符串 assert.isFalse(isValidSchema({ username: true, posts: 75, verified: true, role: creator, supporter: true, badges: [ veteran ] }));可以归纳出七种失败模式各自对应一条 Schema 约束元素类型错误用例 4、必需字段缺失用例 5、9、可选字段类型错误用例 6、联合类型取值越界用例 7、布尔字段被字符串伪装用例 8、字符串字段被布尔值伪装用例 10。这意味着一个正确的实现必须逐项独立检查任何一项短路失败都应返回false。参考解法逐行解析原挑战--solutions--段提供的参考实现function isValidSchema(obj) { const roles [user, creator, moderator, staff, admin]; return ( typeof obj.username string typeof obj.posts number typeof obj.verified boolean roles.includes(obj.role) (obj.supporter undefined || typeof obj.supporter boolean) Array.isArray(obj.badges) obj.badges.every(b typeof b string) ); }关键实现要点typeof做基础类型检查username、posts、verified分别对应string、number、boolean。注意typeof null object、typeof NaN number等 JavaScript 特性在此场景下不影响测试用例但生产代码中若需严格排除NaN可补充Number.isFinite(obj.posts)。联合类型用白名单数组 includesroles.includes(obj.role)是字面量联合最直接的运行时翻译等价的Set写法const roles new Set([...]); roles.has(obj.role)在大枚举下查询更快本例五个值用数组即可。可选字段的标准译法(obj.supporter undefined || typeof obj.supporter boolean)是 TypeScript 中supporter?: boolean的精确运行时等价物——缺省放行出现则严格校验类型。这也是用例 6supporter: no与用例 2supporter: true必须一真一假的原因。数组检查的两段式先Array.isArray(obj.badges)确认是数组同时覆盖缺失字段因为Array.isArray(undefined)为false用例 5 由此失败再.every(b typeof b string)校验元素。空数组时every返回true恰好实现(or empty)语义。短路求值顺序链从前往后短路任一失败即整体false因此无需显式else分支六个条件构成六个独立的守门条件。没有显式检查额外键解法从不枚举Object.keys(obj)因此followers等任意多余键天然通过与 Extra keys are allowed 规则一致。从源码结构看该挑战如何被课程系统识别从 challenge-types.ts 的源码结构可以确认该挑战在课程体系中的完整链路dailyChallengeJs值为 28注册进challengeTypes导出对象第 30、67 行viewTypes映射中[dailyChallengeJs]: classic即前端使用经典编辑器视图渲染该挑战submitTypes映射中[dailyChallengeJs]: tests即提交时执行测试断言而非收集项目 URLgetIsDailyCodingChallenge()第 187-188 行通过数组dailyCodingChallengeTypes同时识别 JS28与 Python29两类供每日挑战的展示、归档与今日挑战路由逻辑复用getDailyCodingChallengeLanguage()将 28 映射为javascript决定编辑器语言高亮与种子代码注入。结合 块配置 中disableLoopProtectTests: true可以推断本系列挑战允许提交含潜在无限循环的代码判定完全依赖测试断言与执行超时而非循环检测器拦截。对学习者而言这也意味着校验函数中若误写while循环可能不会立刻报错而会卡住测试运行。常见陷阱与扩展方向基于上述用例与参考解法实现时的典型错误有三种把可选字段当必需字段写成typeof obj.supporter boolean会导致缺失supporter的合法对象若测试中存在误判为false。正确姿势永远是 undefined ||前置。用或真值判断代替类型判断例如if (obj.verified)会把用例 3 的verified: false判为非法if (obj.posts)会把posts: 0判为非法。必须使用typeof精确匹配。只检查数组不检查元素Array.isArray(obj.badges)单独成立时用例 4[first-post, 18]会漏放必须叠加.every()。扩展方向上可以顺着 Schema Validator 系列Part 1 至 Part 6逐步提高复杂度Part 5 的这套白名单 可选 数组组合已经覆盖了手写校验器的三大核心构件。若需进一步泛化可以把 Schema 本身数据化例如require: {username: string, posts: number}、optional: {supporter: boolean}、union: {role: [...]}、arrayOf: {badges: string}写一个按描述递归遍历的通用校验函数——这正是 JSON Schema 类库的核心思路也是本系列后续挑战值得尝试的方向。小结本挑战Challenge 299: Schema Validator Part 5的价值在于把 TypeScript 风格的类型标注翻译成纯 JavaScript 的运行时检查联合类型对应白名单includes可选字段对应 undefined ||的短路放行string[]对应Array.isArray加every双段检查允许额外键对应不枚举键名的宽松匹配。10 组测试用例恰好构成一张覆盖矩阵任何一处条件写错都会有一组断言立刻暴露问题是练习结构化防御性编程的高质量训练题。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考