前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载本指南以 Relay 仓库中packages/relay-e2e-test/fixtures/typechecking/unselected-field.md端到端测试为线索系统讲解组件读取了查询未选中的字段这一经典场景GraphQL 文档合法、Relay 编译器通过但生成的AppTestQuery$data类型中根本不存在该字段唯有 TypeScript 类型检查能拦截。读完本文你将掌握 Relay 生成类型与运行时行为之间的边界、e2e 快照测试管线的执行链路以及如何在自己的项目中用类型系统兜住这类静默 undefined隐患。场景背景什么是最典型的未选中字段访问在 Relay 应用中每个graphql标签包裹的查询、片段都会被编译器转换成对应的 TypeScript 类型AppTestQuery$data等。类型中只包含查询中显式选中的字段——这是 Relay 数据模型的核心约定声明了什么类型里才有什么。本 fixture 演示的正是踩坑点组件代码读取了一个查询没有选中的字段。这里farewell存在于Query类型上服务端 schema 中确实有该字段因此GraphQL 文档合法farewell是 schema 中的真实字段服务端校验不会报错Relay 编译器无感知编译器只负责生成选中字段的类型它不关心你组件里有没有读多余字段类型检查成为唯一防线由于farewell不在生成的AppTestQuery$data中TypeScript 会报Property farewell does not exist。而运行时行为则更隐蔽未选中的字段在数据对象中就是undefined渲染时表现为空白。组件依然能正常工作、断言依然通过——这正是为什么这个 fixture 存在证明类型检查是唯一能捕获该问题的环节。完整 fixture 拆解从配置到组件Relay 配置fixture 使用标准 TypeScript 语言的 Relay 配置{ src: ./, schema: ./schema.graphql, language: typescript }三个字段的作用分别是src指定源码根目录./schema指向由 Grats 生成的 schema 文件language: typescript让编译器产出.graphql.ts类型的生成产物这正是后续类型检查能工作的基础。服务端 schemagqlQueryField注解该 e2e 测试套件使用 Grats 通过 TypeScript 源码直接推导 GraphQL schema。服务端只定义了两个查询字段/** gqlQueryField */ export function greeting(): string { return Hello, Jordan!; } /** gqlQueryField */ export function farewell(): string { return Goodbye, Jordan!; }注意farewell是真实存在的 schema 字段——这正是文档合法的前提。若它根本不存在于 schemaGraphQL 校验阶段就会直接报错它存在而未被选中才是类型系统需要兜底的情形。App 组件读取未选中字段import { Suspense } from react; import { RelayEnvironmentProvider, useLazyLoadQuery } from react-relay; import { graphql, Environment } from relay-runtime; import { gratsNetwork } from ../GratsNetwork; import { AppTestQuery } from ./__generated__/AppTestQuery.graphql; const testEnvironment new Environment({ network: gratsNetwork }); function Greeting() { const data useLazyLoadQueryAppTestQuery( graphql query AppTestQuery { greeting } , {}, ); // farewell is never selected above, so this is a type error. return ( div {data.greeting} {data.farewell} /div ); } export default function TestApp() { return ( RelayEnvironmentProvider environment{testEnvironment} Suspense fallback{divLoading.../div} Greeting / /Suspense /RelayEnvironmentProvider ); }关键点一目了然查询AppTestQuery只选中了greeting而 JSX 中同时渲染了data.greeting与data.farewell。useLazyLoadQueryAppTestQuery中的泛型参数让data被推断为编译器生成的AppTestQuery$data类型其中不含farewell属性。交互步骤wait Hello, Jordan!该 DSL 步骤表示等待页面上出现文本Hello, Jordan!。它验证了一个反直觉的事实——即便代码里有类型错误应用依然能正常渲染因为farewell运行时只是undefined渲染为空。预期结果快照中的类型错误与 HTML每个 fixture 配套一个.snap.md快照文件记录测试的确定性输出。unselected-field.snap.md给出了本场景的验收标准## Type Errors template/App.tsx(22,13): error TS2339: Property farewell does not exist on type AppTestQuery$data. ## HTML divHello, Jordan!/div这份快照包含两层含义## Type Errors段tsc报出TS2339错误定位在App.tsx第 22 行第 13 列——正是data.farewell的位置。AppTestQuery$data类型中确实不存在farewell属性## HTML段最终渲染结果为divHello, Jordan!/divfarewell的undefined在 JSX 中不产生任何输出。类型错误与正常运行并存正是该场景最值得警惕之处。端到端测试管线快照是如何产生的上述结果并非手写而是由packages/relay-e2e-test测试框架自动生成并与快照比对。整条管线在 fixtures-test.js 中驱动核心是 runFixture.js 中的三步执行链第一步Grats 生成 schemaawait run(gratsBin, [--tsconfig, tsconfigPath]);用 fixture 中的server.ts源码生成schema.graphql与可执行的schema.ts。测试期间的网络层 GratsNetwork.ts 直接使用graphql包的execute/subscribe对着这份内存 schema 执行查询无需真实 HTTP 服务。第二步relay-compiler 生成类型产物await run(relayBin, [relayConfigPath], { env: {FORCE_NO_WATCHMAN: 1}, cwd: path.join(tempDir, template), });编译器读取relay.config.json产出__generated__/AppTestQuery.graphql.ts。这份产物是类型检查的基础——它定义了什么字段存在于AppTestQuery$data。编译器二进制的解析顺序见 README.md优先RELAY_COMPILER_BINARY环境变量其次本地compiler/target/debug/relayRust 编译器构建产物最后回退到 npm 包CI 环境下回退会被视为硬错误。第三步tsc 类型检查tscBin, [-p, tsconfig.json, --noEmit, --pretty, false]tsc在临时目录中对 fixture 连同第二步生成的产物做全量类型检查。其中--pretty false保证每条诊断单行输出、无颜色码方便进入快照。这里的设计决策值得注意见 runFixture.js诊断不抛错而是进入快照类型错误写入## Type Errors段让类型缺陷保持可见、可评审而不阻断那些用于刻画特定行为的 fixture空输出 非零退出码视为测试框架故障若tsc非零退出却无任何诊断输出说明是 harness 自身崩溃绝不静默当成无类型错误写进快照路径脱敏将relay-runtime、react-relay包的绝对路径替换为relay-runtime、react-relay占位符保证快照在仓库内部布局oss/name与开源布局packages/name之间可共享。快照对比语义fixtures-test.js 将实际输出与unselected-field.snap.md逐字对比遵循 Jest 快照语义yarn test:e2e -u重写变更的快照yarn test:e2e --ci绝不写入快照新增或变更的快照视为失败新 fixture 在普通本地运行时会生成.snap.md需与 fixture 一起提交。类型错误段仅在存在错误时才出现本 fixture 若将来某天编译器为未选中字段生成了类型## Type Errors段消失快照比对立即失败——这从测试层面保证了未选中字段绝不出现在类型中这一约束不被悄悄放宽。类型检查生效的底层条件tsconfig 的paths映射为什么tsc能精确命中仓库内relay-runtime、react-relay的类型声明关键在于 setupTempDir.js 生成的临时tsconfig.json临时目录位于仓库之外因此paths必须写入指向本仓库两个包的绝对路径paths[name] [path.join(root, index.d.ts)]; paths[${name}/lib/*] [path.join(root, *)]; paths[${name}/*] [path.join(root, *)];lib/*条目让源码树看起来像已发布包npm 发布时文件被拍平到lib/下源码中是relay-runtime/network/RelayObservable而发布形态是relay-runtime/lib/network/RelayObservabletsc优先匹配更具体的模式因此lib/*先命中裸*条目只兜底已经扁平化的路径。由此fixture 的useLazyLoadQueryAppTestQuery是对着本仓库此提交实际携带的.d.ts声明做校验而非 npm 上已发布的旧版本。README 明确指出仓库另一个检查yarn typecheck:ts只验证relay-runtime声明文件内部自洽而 e2e 套件才真正验证它们可被实际使用。这正是本 fixture 的独特价值——它以真实可运行的组件持续验证生成类型对外暴露的形状。实战启示如何在自己项目中利用这条防线结合本 fixture可提炼出可落地的工程经验把读取字段与选中字段视为同一件事Relay 的类型即契约。data.xxx中xxx必须来自某个graphql查询或片段的选中字段反模式是依赖运行时碰巧存在的数据undefined会静默吞掉渲染缺陷。正确使用泛型与生成产物useLazyLoadQueryAppTestQuery中的泛型让data收窄为AppTestQuery$data。若省略泛型或改用手写接口类型防线即刻失效。确保编译器开启language: typescript并提交__generated__产物或纳入 CI 生成。警惕schema 有字段 ≠ 数据对象有字段只要字段没被查询选中服务端就不会返回它。本 fixture 证明greeting与farewell同时存在于 schema但数据中只有前者——schema 校验与类型检查是两道不同的防线。用 e2e 快照固化类型行为可以在自己的 Relay 项目中复刻这一模式——为类型错误即验收标准的场景建立快照测试将tsc诊断输出纳入断言防止回归。具体运行方式见 README.md单跑本 fixture 可用yarn test:e2e -- --testNamePattern unselected-field修改编译器后需先用cargo build --manifest-pathcompiler/Cargo.toml --bin relay构建本地编译器二进制再执行测试。理解错误信息的结构error TS2339: Property farewell does not exist on type AppTestQuery$data是修复线索的浓缩——先检查graphql文档是否选中了该字段再检查useLazyLoadQuery/useFragment的泛型是否指向正确的生成类型。结语unselected-field端到端测试以最小可运行的形式完整展示了 Relay 类型安全机制的关键边界编译器只为你声明的内容生成类型运行时只返回你查询的内容中间的任何错位都只能由 TypeScript 捕获。理解这条边界既能解释生产中字段渲染为空却不报错的疑难杂症也能让你把 Relay 的类型系统从锦上添花升级为数据访问的强制执行者。深入阅读仓库源码 runFixture.js 与 fixtures-test.js即可完整复现并扩展这一测试模式。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐Atom-7B-Chat-openmind社区生态如何参与Llama中文社区共建Atom 7B Chat openmind社区生态如何参与Llama中文社区共建 Atom 7B Chat openmind是由Llama中文社区和AtomE前端开发工具Relay Type Emission 指南relay-compiler 如何生成类型安全的 Flow 与 TypeScript 代码Relay Type Emission 指南relay compiler 如何生成类型安全的 Flow 与 TypeScript 代码 Relay Compi前端开发工具Relay Compiler 的 interner crateRust 字符串与任意类型驻留Intern机制深度解析Relay Compiler 的 interner crateRust 字符串与任意类型驻留Intern机制深度解析 导读 compiler/crates前端开发工具上一篇Box86架构深度解析ARM平台x86用户态模拟器的技术实现下一篇LinuxKit构建安全容器操作系统的革命性工具包创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考