ARTICLE DETAIL

资讯详情

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

Flow 静态类型检查中的 Relay 集成评估环境:relay_core Eval Context 设计与实现

Flow 静态类型检查中的 Relay 集成评估环境:relay_core Eval Context 设计与实现 Flow 静态类型检查中的 Relay 集成评估环境relay_core Eval Context 设计与实现【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址: https://gitcode.com/gh_mirrors/flow30/flow导读本文围绕 Flow 仓库中 evals/evals/03_project_patterns/relay_core/README.md 所定义的relay_core评估上下文展开剖析这套用于真实项目模式Real-World Project Patterns类别评估的独立 Flow 工程它如何通过手写的 Relay 类型存根、relay_integration配置与graphql产物类型文件让 AI 模型在无需引入完整 relay-compiler 的前提下编写出能够通过flow full-check零错误校验的 Relay Flow 组件。读完本文你将掌握该评估环境的目录组织、存根类型的设计意图、relay_integration的解析机制以及新增评估 prompt 的标准流程。背景为什么需要一个独立的 Relay 评估根Flow 的评估体系evals按类别组织其中 Category 3 专门针对真实世界项目模式。要评估模型能否产出正确的 Relay Flow 组件代码最直接的办法是搭建一个独立的 Flow 根目录standalone Flow root里面只放置必要的 Relay 类型存根与手写的生成产物类型让模型在这个沙箱里自由编辑文件并接受类型检查。relay_core正是这样一个沙箱它不依赖真实安装的relay-compiler也不需要真实的 GraphQL Schema 与代码生成流程而是用一套最小化但语义准确的类型存根还原出 wwwMeta 内部环境中 Relay Flow 的典型类型推导行为。这样做的价值在于评估时模型只需编写组件源码无需处理 Node 依赖安装与编译流程类型检查结果零错误/有错误可以自动化判定模型输出是否正确存根设计刻意复刻了relay_integrationtrue下graphql标签的解析语义使评估结论对真实工程具备参考意义。目录结构总览relay_core的完整结构如下见 READMErelay_core/ └── context/ # Standalone Flow root — models edit files here ├── .flowconfig ├── libdefs_xplat/ # Minimal libdefs needed by the evals ├── RelayHooks.js # Declare stub for all relay hooks PreloadedQuery type ├── relay-runtime/ │ ├── package.json # haste_commonjs:true — resolves relay-runtime module name │ └── index.js # Declare stubs: OperationType, FragmentType, etc. └── *.graphql.js # Hand-authored generated query/fragment types per eval其中context/就是模型进行编辑的 Flow 根所有评估相关的类型存根与手写生成产物都放在这里。除context/之外仓库内还配套有candidate_prompts/每个评估任务的 prompt 文件与ideals/理想组件实现用于校验例如candidate_prompts/01_notification_inbox.mdcandidate_prompts/02_activity_feed.mdideals/01_notification_inbox/NotificationInbox.react.jsideals/02_activity_feed/ActivityFeed.react.js类型设计核心一RelayHooks.js 中的 Hook 存根RelayHooks.js 是该评估环境最关键的声明文件。它声明了graphql标签函数与一组 Relay HooksusePreloadedQuery、useFragment、useMutation、useLazyLoadQuery全部基于从relay-runtime导入的基础类型展开。以下是文件的完整存根内容import type { Disposable, FragmentType, GraphQLTaggedNode, MutationParameters, OperationType, PayloadError, } from relay-runtime; // Opaque type for a preloaded query reference. export opaque type PreloadedQueryout TQuery extends OperationType: Readonly{ variables: TQuery[variables], ... } Readonly{ variables: TQuery[variables], __id: string, ... }; export type UseMutationConfigTMutation extends MutationParameters { variables: TMutation[variables], onCompleted?: ?( response: TMutation[response], errors: ?ReadonlyArrayPayloadError, ) void, onError?: ?(error: Error) void, ... }; declare export function graphql( strings: ReadonlyArraystring, ): GraphQLTaggedNode; declare export hook usePreloadedQueryTQuery extends OperationType( query: GraphQLTaggedNode, queryRef: PreloadedQueryTQuery, ): TQuery[response]; // TData is inferred from the keys $data property (populated by relay-compiler). declare export hook useFragment TFragmentType extends FragmentType, TKey extends Readonly{ $fragmentSpreads: TFragmentType, $data?: unknown, ... }, ( fragment: GraphQLTaggedNode, key: TKey, ): NonNullableTKey[$data]; declare export hook useMutationTMutation extends MutationParameters( mutation: GraphQLTaggedNode, ): [ commit: (config: UseMutationConfigTMutation) Disposable, isPending: boolean, ]; declare export hook useLazyLoadQueryTQuery extends OperationType( query: GraphQLTaggedNode, variables: TQuery[variables], ): TQuery[response];useFragment的$data索引访问推导README 特别强调了一个设计要点useFragment的返回类型是通过**索引访问类型indexed access**从 key 的$data属性推导出来的即NonNullableTKey[$data]。这带来两个直接约束.graphql.js片段 key 类型必须包含$data?: DataType这一可选属性否则TKey[$data]无法解析出数据形状调用useFragment(fragment, key)时Flow 会从传入的 key 反推TData无需显式指定类型参数。以实际的 NotificationItem_notification.graphql.js 为例其$key类型就严格遵循了这一约定export type NotificationItem_notification$key Readonly{ $data?: NotificationItem_notification$data, $fragmentSpreads: NotificationItem_notification$fragmentType, ... };而在理想实现 NotificationItem.react.js 中组件直接消费这一 key 类型useFragment自动返回$data的完整形状import type {NotificationItem_notification$key} from NotificationItem_notification.graphql; import {graphql, useFragment} from RelayHooks; const notificationFragment graphql fragment NotificationItem_notification on Notification { title timestamp isRead } ; export default component NotificationItem( notificationRef: NotificationItem_notification$key, ) { const notification useFragment(notificationFragment, notificationRef); return ( div strong{notification.title}/strong span{notification.timestamp}/span span{notification.isRead ? Read : Unread}/span /div ); }PreloadedQuery的不透明类型封装PreloadedQuery被声明为opaque type并带有out TQuery extends OperationType的协变类型参数与experimental.opaque_type_new_bound_syntaxtrue的新式上界语法见下文 .flowconfig 分析。外部可见的上界暴露了variables而实际表示representation中还包含__id字段。组件层面只能看到variables这正是对真实PreloadedQuery语义的近似——调用方拿到的是一个已经预加载的查询引用无需关心内部实现细节。类型设计核心二relay-runtime 存根与模块解析最小化的 runtime 类型relay-runtime/index.js 只声明了评估所需的最小类型集文件头部注释也明确写着 Minimal type stubs for relay-runtime, used in relay_core evalsexport type OperationType Readonly{ variables: {...}, response: {...}, ... }; export interface FragmentType {} export type MutationParameters { variables: {...}, response: {...}, rawResponse?: {...}, ... }; export type GraphQLTaggedNode Readonly{ kind: string, ... }; export type Disposable { dispose: () void, ... }; export type PayloadError { message: string, locations?: ReadonlyArray{line: number, column: number, ...}, ... };注意GraphQLTaggedNode被定义为Readonly{kind: string, ...}——只读且只有一个kind: string字段。这个看似简单的定义是支撑relay_integration解析机制的关键详见下一节。haste_commonjs 与 haste 路径排除relay-runtime/package.json 的内容为{ name: relay-runtime, haste_commonjs: true, main: index.js }README 明确指出relay-runtime/目录被排除在 haste 路径扫描之外haste.paths.excludes这样模块名relay-runtime才能通过 package.json 的haste_commonjs机制解析——而不是被当作 haste 模块按文件路径注册。这与真实项目中import ... from relay-runtime的解析方式保持一致让模型写的 import 语句与生产代码完全同构。对应到 .flowconfig 中的配置是module.systemhaste module.system.haste.module_ref_prefixm# module.system.haste.paths.excludes.*/__tests__/.* module.system.haste.paths.excludes.*/__mocks__/.* module.system.haste.paths.excludesPROJECT_ROOT/relay-runtime/.*relay_integrationgraphql 标签如何解析到生成产物这是整个评估环境类型推导正确性的核心机制。README 说明评估环境开启了relay_integrationtrue与 www 保持一致to match www开启后graphql标签模板字符串在**调用点call site**会被 Flow 解析为与之匹配的.graphql.js产物模块的导出而不是声明文件RelayHooks.js中graphql函数声明的返回类型GraphQLTaggedNode因此模型无需显式 import 查询/片段的类型——只要写了graphql标签Flow 就能自动关联到对应的生成文件。要让被解析到的产物模块满足 Hooks 的GraphQLTaggedNode参数约束每个产物存根都必须导出一个运行时kind哨兵常量查询query产物导出export const kind: Request Request片段fragment产物导出export const kind: Fragment Fragment。同时relay-runtime中的GraphQLTaggedNode被定义为Readonly{kind: string, ...}其中kind为只读——这恰好与const导出的只读语义匹配。const kind: Request Request的类型是字面量类型Request可以赋值给string从而满足GraphQLTaggedNode。查询产物NotificationInboxQuery.graphql.js以 NotificationInboxQuery.graphql.js 为例可以看到查询产物同时具备变量/响应类型与kind 哨兵两部分import type {NotificationItem_notification$fragmentType} from NotificationItem_notification.graphql; export type NotificationInboxQuery$variables {userId: string}; export type NotificationInboxQuery$data Readonly{ notifications: ReadonlyArray Readonly{ id: string, $fragmentSpreads: NotificationItem_notification$fragmentType, }, , }; export type NotificationInboxQuery Readonly{ variables: NotificationInboxQuery$variables, response: NotificationInboxQuery$data, }; // Runtime sentinel required by relay_integration so the graphql tag resolves // to a GraphQLTaggedNode-compatible type. export const kind: Request Request;片段产物NotificationItem_notification.graphql.js片段产物则额外定义$fragmentType、$data与$key三种类型其中$key的$data?可选属性正是useFragment索引访问推导的数据来源import type {FragmentType} from relay-runtime; declare export opaque type NotificationItem_notification$fragmentType: FragmentType; export type NotificationItem_notification$data Readonly{ title: string, timestamp: string, isRead: boolean, $fragmentType: NotificationItem_notification$fragmentType, }; export type NotificationItem_notification$key Readonly{ $data?: NotificationItem_notification$data, $fragmentSpreads: NotificationItem_notification$fragmentType, ... }; // Runtime sentinel required by relay_integration so the graphql tag resolves // to a GraphQLTaggedNode-compatible type. export const kind: Fragment Fragment;.flowconfig一份贴近 www 的 Flow 配置.flowconfig 完整还原了生产级 Flow 工程的配置面貌逐段分析如下[ignore] .*/__tests__/.* .*/__mocks__/.* .*/__benchmarks__/.* [declarations] # Do NOT add entries here. Fix type stubs instead. [include] [options] module.systemhaste module.system.haste.module_ref_prefixm# module.system.haste.paths.excludes.*/__tests__/.* module.system.haste.paths.excludes.*/__mocks__/.* module.system.haste.paths.excludesPROJECT_ROOT/relay-runtime/.* facebook.fbsFbs facebook.fbtFbtElement munge_underscorestrue relay_integrationtrue experimental.opaque_type_new_bound_syntaxtrue experimental.module.automatic_require_defaulttrue experimental.facebook_module_interoptrue experimental.strict_es6_import_exporttrue babel_loose_array_spreadtrue react.runtimeclassic ban_spread_key_propstrue format.bracket_spacingfalse format.single_quotestrue [lints] deprecated-typeerror untyped-type-importerror unused-promiseerror [version] 0.317.0各配置项的作用配置项作用module.systemhaste使用 haste 模块系统与 Meta 内部工程一致module.system.haste.module_ref_prefixm#haste 模块引用前缀m#module.system.haste.paths.excludesPROJECT_ROOT/relay-runtime/.*将relay-runtime排除出 haste 路径扫描使其通过haste_commonjs解析facebook.fbsFbs/facebook.fbtFbtElement启用 FBS/FBT 相关内建类型映射munge_underscorestrue启用下划线属性名改写与 tests/config_munging_underscores 等测试目录验证的行为一致relay_integrationtrue核心开关让graphql标签在调用点解析到对应.graphql.js产物模块的导出experimental.opaque_type_new_bound_syntaxtrue允许opaque type ... : Bound Repr的新式上界语法PreloadedQuery依赖此语法experimental.module.automatic_require_defaulttrueCommonJS 模块自动添加默认导出experimental.facebook_module_interoptrueFacebook 模块互操作与 tests/facebook_module_interop 对应experimental.strict_es6_import_exporttrue严格 ES6 import/export 检查babel_loose_array_spreadtrue数组展开采用 loose 语义react.runtimeclassicReact 运行时为 classic非 automatic JSX transformban_spread_key_propstrue禁止在 JSX key 属性上使用展开format.bracket_spacingfalse/format.single_quotestrue代码格式化偏好对象字面量无括号空格、单引号lints中deprecated-type/untyped-type-import/unused-promise均为error三条 lint 以错误级别强制执行[version] 0.317.0要求 Flow 版本不低于 0.317.0[declarations]段中的注释明确提示不要在此处添加条目应当修复类型存根——这是刻意设计评估环境的正确性应通过修好存根来保证而不是用 ignore/declaration 掩盖错误。从理想实现看模型的目标答案评估环境正确性的最终判据是理想组件能否在context/下通过flow full-check。两个已有 eval 的理想实现展示了两种典型的 Relay 组件形态。通知收件箱父子组件 片段引用传递NotificationInbox.react.js 演示了标准的预加载查询 子组件片段引用传递模式——注意它使用了 Flow 的component 语法component关键字并且graphql标签内联写在组件文件里无需 import 任何.graphql.js类型这正是relay_integrationtrue的效果import type {NotificationInboxQuery} from NotificationInboxQuery.graphql; import type {PreloadedQuery} from RelayHooks; import NotificationItem from NotificationItem.react; import {graphql, usePreloadedQuery} from RelayHooks; import * as React from react; const notificationInboxQuery graphql query NotificationInboxQuery($userId: String!) { notifications(userId: $userId) { id ...NotificationItem_notification } } ; export default component NotificationInbox( queryRef: PreloadedQueryNotificationInboxQuery, ) { const data usePreloadedQuery(notificationInboxQuery, queryRef); return ( ul {data.notifications.map(notification ( li key{notification.id} NotificationItem notificationRef{notification} / /li ))} /ul ); }活动流match 表达式 配置类型组合ActivityFeed.react.js 则展示了更复杂的场景联合类型判别__typenamematch表达式、显示选项配置compact、maxItems、showTimestamps独立成ActivityFeedConfig类型文件并通过...ActivityFeedConfig展开组合进 props。对应的查询产物 ActivityFeedQuery.graphql.js 中activityFeed是一个包含PostActivity、FriendRequestActivity、EventReminderActivity、LikeActivity、CommentActivity以及%other兜底分支的判别联合match表达式可以穷尽地、类型安全地处理每一种条目例如match (entry) { {__typename: PostActivity, visibility: public | friends, const author, const preview} compact true ? ${author} posted : ${author} posted: ${preview}, {__typename: PostActivity, visibility: private, const author, ...} ${author} shared a private post, {__typename: FriendRequestActivity, const requester, const mutualFriendCount, group: {const name}} ${requester} sent a friend request — ${mutualFriendCount} mutual friend${mutualFriendCount 1 ? : s} in ${name}, ... _ New activity, }match表达式配合%other兜底分支正是 Flow 新版模式匹配tests/match、tests/match_exhaustive在真实项目模式中的落地用法。新增一个评估任务的标准流程README 给出了清晰的四步流程任何新增的 Relay 评估场景都按此执行编写.graphql.js类型文件在context/下为新的场景手写查询/片段产物类型包含$variables、$data、$key、kind哨兵等编写 prompt在candidate_prompts/NN_name.md中书写任务描述可选编写理想组件并验证在context/中编写理想组件文件用flow full-check确认零错误提交前清理移除理想文件或移入独立的ideals/目录确保提交的内容只包含上下文与 prompt。candidate_prompts/01_notification_inbox.md与candidate_prompts/02_activity_feed.md就是这一流程的现成范例——两者的 prompt 都明确要求模型使用flow strict-local、使用component语法、从RelayHooks导入 Hooks并以flow零错误作为验收标准。上下文洁净度验证评估环境的可靠性依赖于一个前提在没有模型文件时context 本身必须是零错误的。README 给出了标准的验证命令cd context flow full-check --show-all-errors--show-all-errors用于展示全部错误而非默认的截断输出确保任何存根设计缺陷都会被立即发现。这条命令应该作为每次修改存根或新增产物类型后的回归检查来执行——只有在裸 context零错误的前提下后续模型代码产生的错误才能被准确归因于模型输出而不是评估环境自身的问题。总结relay_core评估环境的设计精髓可以概括为三点最小化存根最大化语义保真RelayHooks.js与relay-runtime/index.js用不到百行声明还原了usePreloadedQuery、useFragment等 Hook 的类型推导行为尤其是通过NonNullableTKey[$data]索引访问推导片段数据的机制relay_integrationtruekind哨兵 haste_commonjs的组合让graphql标签在调用点自动解析到.graphql.js产物导出使模型书写的代码与生产环境 Relay 工程完全同构且无需显式 import 类型可验证性优先[declarations]段禁止堆叠 ignore、flow full-check --show-all-errors作为洁净度回归手段保证评估信号干净可信。如果你要为本仓库新增 Relay 类评估任务直接按照上述四步流程参考candidate_prompts/与ideals/中的现有实现即可快速上手。【免费下载链接】flowAdds static typing to JavaScript to improve developer productivity and code quality.项目地址: https://gitcode.com/gh_mirrors/flow30/flow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表