ARTICLE DETAIL

资讯详情

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

Refine 与 TypeScript Omit 类型:掌握对象类型裁剪,写出更精简的代码

Refine 与 TypeScript Omit 类型:掌握对象类型裁剪,写出更精简的代码 Refine 与 TypeScript Omit 类型掌握对象类型裁剪写出更精简的代码【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读OmitType, Keys是 TypeScript 内置的实用类型之一它允许你从既有类型中移除指定的属性从而派生出新的类型。本文以本仓库refine一个用于构建内部工具、后台面板与 B2B 应用的 React 框架的官方技术博客为蓝本通过Subscriber、PublicUser、FrontendUser等完整示例讲解Omit的用法并深入 refine 各包源码展示Omit在真实框架代码中如何被用来裁剪 react-query 选项、Pagination 与按钮组件 props。读完本文你将掌握Omit的语法、与Pick的取舍、编译期与运行时的差异以及如何在日常开发与开源项目维护中用它精简类型。什么是 TypeScriptOmitType, KeysTL;DROmitType, Keys通过从既有类型中删除某些属性来构造一个新类型。当某些属性不需要时它是减少冗余代码的绝佳工具。与Pick类似Omit接受两个参数第一个参数是基础类型Type第二个参数是要省略的属性键的联合类型Keys多个键用|分隔返回值是排除了这些属性后的派生类型。本篇文章属于《TypeScript Utility Type Series》系列的第二篇前一篇《A Guide on TypeScript Pick Type》见 documentation/blog/2024-12-17-typescript-pick.md。上一篇中我们用Pick从基础类型SuperbUser中挑选少量属性派生出GuestUser并提示当需要保留的属性数量多于需要丢弃的属性数量时Pick反而不太方便此时应优先考虑Omit。本篇我们就围绕这一点用Omit为Subscriber订阅者实体创建类型。TypeScriptOmitType, Keys实战示例假设我们有一张用户关系表其中SuperbUser实体拥有以下属性type SuperbUser { userId: number; macAddress: string; username: string; email: string; password: string; firstName: string; lastName: string; roles: (Admin | Editor | Author)[]; };而Subscriber实体拥有与SuperbUser完全相同的属性——唯独没有roles。如果使用Pick我们需要把除了roles之外的所有属性逐一列出而使用Omit只需要指定要删除的那一个键type Subscriber OmitSuperbUser, roles;这种情况下Omit比Pick更方便。现在我们可以创建一个不再包含roles字段的订阅者对象const subscriber: Subscriber { userId: 4, macAddress: a:5ub:mach1ne, username: sub_user, email: sub_usergmail.com, password: 12345678, firstName: Abdullah, lastName: Numan, }; console.log(subscriber); /* { userId: 4, macAddress: a:5ub:mach1ne, username: sub_user, email: sub_usergmail.com, password: 12345678, firstName: Abdullah, lastName: Numan } */ console.log(subscriber.roles); // undefined注意subscriber.roles在类型层面已被移除访问它返回undefined。如果我们强行在开发期给它赋值subscriber.roles [Reader, Commenter]; // Property roles does not exist on type Subscriber.TypeScript 会立刻报错// Property roles does not exist on type Subscriber.但有趣的是如果我们此时再看console.log(subscriber.roles)会发现这个赋值实际上在运行时真的生效了。也就是说Omit只是开发期的类型约束TypeScript 并不会在编译成 JavaScript 之后继续检查代码的任何后果。JavaScript 引擎会照常把subscriber.roles的值设置进去。重要提醒Omit是开发期编译期的工具不是运行时防护。务必结合 linter如typescript-eslint与类型检查来严格执行 omit 约束防止敏感字段意外暴露。TypeScriptOmit与 Interface与Pick一样基础类型也可以是interface结果完全相同interface SuperbUser { userId: number; macAddress: string; username: string; email: string; password: string; firstName: string; lastName: string; roles: (Admin | Editor | Author)[]; } type Subscriber OmitSuperbUser, roles; const subscriber: Subscriber { userId: 4, macAddress: a:5ub:mach1ne, username: sub_user, email: sub_usergmail.com, password: 12345678, firstName: Abdullah, lastName: Numan, }; console.log(subscriber); /* { userId: 4, macAddress: a:5ub:mach1ne, username: sub_user, email: sub_usergmail.com, password: 12345678, firstName: Abdullah, lastName: Numan } */ console.log(subscriber.roles); // undefined无论基础类型是type还是interfaceOmit的第二个参数都接受属性键的联合类型多个键用管道符|连接type Subscriber OmitSuperbUser, roles | firstName | ...;何时应该避免使用Omit与Pick相对的取舍规则同样成立当需要省略的属性多于需要保留的属性时应避免使用Omit转而使用Pick。例如如果SuperbUser有 8 个属性而你只需要其中的 2 个那么Omit需要列出 6 个要删除的键远不如Pick列出 2 个要保留的键来得直观、不易出错。选择标准很简单——保留的少用Pick丢弃的少用Omit。何时应该使用 TypeScriptOmitOmit的核心价值在于保留大部分字段、只剔除少数几个。典型场景包括移除敏感字段例如从用户对象中剔除password、passwordHash、createdAt等为复杂类型生成简化版本基础类型很庞大但某些场景只需要其中绝大部分字段快速适配第三方类型不想手写一个全新的类型只想对现有类型做微调。下面逐一展开。简化派生类型当你有一个复杂的基础类型但需要一个去掉少数字段的简化版本时Omit非常方便type FullUser { id: number; name: string; email: string; password: string; createdAt: Date; }; // 创建一个不含敏感数据password、createdAt的 PublicUser 类型 type PublicUser OmitFullUser, password | createdAt; const user: PublicUser { id: 1, name: John Doe, email: johndoegmail.com, }; console.log(user); /* Output: { id: 1, name: John Doe, email: johndoegmail.com } */API 数据过滤有时候 API 或后端会返回完整的对象而前端只需要其中一小部分字段。你可以手写一个全新的类型也可以直接用Omit快速裁剪interface ApiResponse { id: number; username: string; email: string; passwordHash: string; isAdmin: boolean; } // 创建一个不含敏感后端数据的 FrontendUser 类型 type FrontendUser OmitApiResponse, passwordHash | isAdmin; const frontendUser: FrontendUser { id: 101, username: frontend_dev, email: devexample.com, };为特定上下文创建更干净的类型当你在处理表单、UI 组件或其他模块时往往只需要父类型中的部分字段。Omit能让你的类型保持整洁和聚焦interface FullProduct { id: string; name: string; description: string; price: number; createdAt: Date; updatedAt: Date; } // 为 UI 表单创建不含元数据的 ProductForm 类型 type ProductForm OmitFullProduct, id | createdAt | updatedAt; const formData: ProductForm { name: Gaming Laptop, description: A powerful laptop for gaming., price: 1500, };Pick与Omit对比特性PickOmit用途选择特定字段排除特定字段语法PickType, KeysOmitType, Keys适用场景只需要少量字段时只需要省略少量字段时示例type A PickType, id \| name;type B OmitType, password;结果仅包含id和name排除password一句话概括Omit是Pick的“相反等价物”当我们要保留更多、省略更少时Omit更便捷。从源码看Omit在 refine 中的真实应用Omit不只是教程里的玩具它在 refine 这类大型开源框架的源码中无处不在。下面几个例子都来自当前仓库可以帮你直观理解它在真实工程中的价值。裁剪 react-query 的useQuery选项在 packages/core/src/hooks/data/useOne.ts#L50-L69 中useOnehook 的queryOptions是这样定义的queryOptions?: Omit UseQueryOptions GetOneResponseTQueryFnData, TError, GetOneResponseTData , queryKey | queryFn { // Make queryKey and queryFn optional queryKey?: UseQueryOptions GetOneResponseTQueryFnData, TError, GetOneResponseTData [queryKey]; queryFn?: UseQueryOptions GetOneResponseTQueryFnData, TError, GetOneResponseTData [queryFn]; };这里 refine 用OmitUseQueryOptions..., queryKey | queryFn从 TanStack Query 的UseQueryOptions类型中剔除queryKey与queryFn因为这两个值由 hook 内部根据resource、id等参数自动计算随后再通过交叉类型把这两个字段以可选形式放回去。这是继承第三方类型、约束内部字段的经典写法。裁剪useMutation选项同理在 packages/core/src/hooks/data/useCreate.ts#L79-L87 中useCreate的mutationOptions通过OmitUseMutationOptions..., mutationFn把 TanStack Query 的mutationFn移除它由 refine 内部绑定到dataProvider.create只允许调用方传入其余配置mutationOptions?: Omit UseMutationOptions CreateResponseTData, TError, UseCreateParamsTData, TError, TVariables, unknown , mutationFn ;useOne、useCreate所在的数据 hooks 目录见 packages/core/src/hooks/data类似的Omit用法还出现在useUpdate、useDelete、useCustom、useInfiniteList等多个 hooks 中。改写Pagination的mode字段在 packages/core/src/hooks/useSelect/index.ts#L111-L119 中useSelect的pagination参数被定义为pagination?: Prettify OmitPagination, mode { /** * Whether to use server side pagination or not. * default off */ mode?: Pagination[mode]; } ;refine 先用OmitPagination, mode去掉Pagination类型里的mode字段再把它重新声明为可选并附上文档注释。这样既保留了原有字段又让内部受控的字段对外暴露为可选项——Omit在此扮演了类型重构的角色。精简按钮组件的 props在 packages/chakra-ui/src/components/buttons/types.ts#L17-L25 中Chakra UI 适配层的按钮类型也大量使用Omitexport type ShowButtonProps Omit RefineShowButtonProps ButtonProps, { svgIconProps?: OmitIconProps, ref; } , ignoreAccessControlProvider ;这里有两层Omit外层OmitRefineShowButtonProps..., ignoreAccessControlProvider从 UI 类型定义中移除框架内部使用的ignoreAccessControlProvider字段避免暴露给外部使用者内层OmitIconProps, ref从 Tabler Icons 的IconProps中剔除ref防止 SVG 图标属性被传入不合法的 ref。这展示了Omit在框架封装层的另一个典型用途隐藏内部实现细节收紧公开 API 的类型面。编译期 vs 运行时为什么 linter 很重要从上面的源码示例可以再次确认Omit的一切约束都发生在编译期。TypeScript 编译成 JavaScript 后类型信息会被完全擦除Omit不会产生任何运行时代码。因此类型层面的删除不等于运行时的删除——像本文开头subscriber.roles [...]这样的赋值在运行时依然有效若要在运行时真正剔除敏感字段需要配合destructuring、pick/omit之类的工具函数或序列化逻辑开发期要依靠 TypeScript 编译检查与 linter 规则例如typescript-eslint的相关配置来确保 omit 约束不被绕过。这也是 refine 在源码中大量使用Omit做 API 面收紧的底气类型错误可以在 CI/编辑器里第一时间暴露而不会等到运行时才发现字段泄漏。结语本文围绕Subscriber实体的派生完整演示了OmitType, Keys的用法它接受基础类型与待删除键的联合类型返回剔除这些属性后的新类型基础类型可以是type也可以是interface当需要省略的属性多于保留的属性时应改用Pick敏感字段剔除、API 数据过滤、UI 表单类型精简都是它的经典应用场景。随后我们通过 refine 源码中的useOne、useCreate、useSelect与 Chakra UI 按钮类型看到了它在真实框架中裁剪第三方类型、隐藏内部字段、收紧公开 API的实战价值。最后务必牢记Omit是编译期工具而非运行时防护Pick与Omit是一对互补的伙伴——保留少用Pick省略少用Omit。本系列的下一篇将介绍利用PartialType进行对象类型转换。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表