尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

type-graphql 类型与字段完全指南:用 `@ObjectType` 与 `@Field` 从 TypeScript 类生成 GraphQL Schema

发布时间:2026/9/27 9:57:19

资讯中心
01
ARTICLE

type-graphql 类型与字段完全指南:用 `@ObjectType` 与 `@Field` 从 TypeScript 类生成 GraphQL Schema

type-graphql 类型与字段完全指南:用 `@ObjectType` 与 `@Field` 从 TypeScript 类生成 GraphQL Schema
后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载TypeGraphQL 的核心设计理念是以代码为单一事实来源不编写手写的 SDL 文件与接口描述而是直接从 TypeScript 类自动生成 GraphQL schema 定义。本篇指南围绕ObjectType与Field两个核心装饰器展开完整讲解对象类型声明、字段映射、泛型/数组类型标注、nullability可空性精细化控制、标量覆盖与属性隐藏等实战技巧帮助你仅靠类与装饰器就能构建出类型安全的 GraphQL 服务端。从 TypeScript 类到 GraphQL Schema 的映射原理TypeGraphQL 摆脱了传统 GraphQL 开发中维护 SDL 文件 编写 resolver 代码的双重维护负担。它的工作方式是用装饰器标注类与属性借助 TypeScript 反射机制收集类型元数据再在运行时生成GraphQLObjectType与 schema。用到的装饰器和反射机制对应源码中的 ObjectType.ts 与 Field.ts 实现。先看一个未装饰的普通 TypeScript 类它代表Recipe数据模型class Recipe { id: string; title: string; ratings: Rate[]; averageRating?: number; }此时它只是普通类TypeGraphQL 对它一无所知。要让这个类成为 GraphQL 输出类型必须分两步激活它。ObjectType()把类标记为 GraphQL 对象类型第一步是为类加上ObjectType()装饰器。它把类标记为 GraphQL SDL 中的type等价于graphql-js中的GraphQLObjectTypeObjectType() class Recipe { id: string; title: string; ratings: Rate[]; averageRating: number; }ObjectType的底层行为可以从 ObjectType.ts 的源码看出它调用getMetadataStorage().collectObjectMetadata({ name, target, description, ... })把类的名字默认取target.name、可选描述、implements的接口列表等收集进全局元数据存储中供后续 schema 生成阶段使用。ObjectType支持三种调用形态从源码的函数重载可以看出ObjectType() // 用类名作为 GraphQL type 名 ObjectType({ description: ... }) // 附加描述等选项 ObjectType(ExternalTypeName) // 自定义对外暴露的 type 名Field()声明哪些属性映射为 GraphQL 字段第二步是逐个声明要暴露的属性。Field()装饰器不仅声明字段还负责从 TypeScript 反射系统design:type元数据收集属性类型ObjectType() class Recipe { Field() id: string; Field() title: string; Field() ratings: Rate[]; Field() averageRating: number; }Field的源码实现Field.ts会读取反射元数据并调用findType见 findType.ts来解析类型若未提供显式类型函数则读取design:type反射元数据若反射类型缺失或属于被禁止的类型如Object、Function等bannedTypes会抛出NoExplicitTypeError提示你必须显式声明类型。因此对于string、boolean、number这类简单类型一个裸的Field()就足够了。泛型类型必须显式标注数组与嵌套数组TypeScript 反射机制存在一个众所周知的限制无法反射泛型参数。Rate[]反射出来的类型只有ArrayTypeGraphQL 无从得知数组元素是Rate。因此凡是涉及泛型如Array、Promise都必须向Field显式提供类型信息。旧版本文档本指南依据的 version-0.17.2给出了两种可用写法// 方式一推荐显式 [ ] 数组语法 Field(type [Rate]) ratings: Rate[]; // 方式二只传元素类型数组由反射推断 Field(itemType Rate) ratings: Rate[];推荐使用第一种。Field(type [Rate])明确声明这是一个元素类型为Rate的数组语义清晰且不受反射限制的干扰第二种依赖反射推断数组维度容易出错。对于嵌套数组当前主分支文档已补充说明直接用多层[ ]表示深度即可例如Field(type [[Int]])表示整数数组的数组深度为 2。在源码 findType.ts 中findTypeValueArrayDepth会递归解析数组嵌套层数并记录arrayDepth这正是多层[[Int]]能被正确解析的实现依据。为什么用函数语法而不是{ type: Rate }因为函数语法thunk是延迟求值的type [Rate]在 schema 生成时才真正执行。这样就能优雅地解决循环依赖问题——比如Post -- User互相引用时若在装饰器求值时立即读取对方类定义很可能拿到尚未定义的变量。如果你为了省几个字符想写Field(() Rate)也是允许的只是对他人可读性略差。这是 TypeGraphQL 社区沿用的约定请保持一致性。可空性Nullability从默认全部非空到精细化控制TypeGraphQL 的默认行为与 TypeScript 一致所有字段默认非空。对应到 SDL 上就是每个字段都带!后缀。如果你希望全项目的字段默认可空可以在buildSchema设置中传入nullableByDefault: true详见 bootstrap 指南。从 build-context.ts 源码可见BuildContext.nullableByDefault默认值为false且可通过该选项覆盖。单个可空字段?:{ nullable: true }对于像averageRating这种评分尚不存在时可能为 undefined的属性需要同时做两件事类属性用?:标记为可选在Field中传入{ nullable: true }。Field({ nullable: true }) averageRating?: number;这里有一个值得注意的陷阱当你把属性声明为可空联合类型如string | null时必须显式给Field提供类型。因为反射系统对联合类型的推断不可靠TS 反射不出联合类型否则会抛出NoExplicitTypeError或生成错误类型。列表的可空性nullable: items与nullable: itemsAndList简单的{ nullable: true | false }只能控制整个列表的可空性生成的是[Item!]列表可空元素非空或[Item!]!整体非空。但有时候你需要稀疏数组——即允许数组中存在null元素。此时要用两个字符串选项选项生成 SDL含义{ nullable: true }/{ nullable: false }[Item!]/[Item!]!仅控制整个列表是否可空元素始终非空{ nullable: items }[Item]!列表整体非空但元素可空稀疏数组{ nullable: itemsAndList }[Item]列表与元素都可空注意如果同时设置了nullableByDefault: true它对列表同样生效会生成[Item]等价于nullable: itemsAndList的效果这一点容易忽略。从源码层面看可空性包装逻辑集中在 helpers/types.ts 的wrapWithTypeOptions中它先根据arrayDepth递归包装GraphQLList再根据nullable与nullableByDefault决定是否包裹GraphQLNonNull。其中还有一道防线如果对非数组字段使用items或itemsAndList会抛出WrongNullableListOptionError防止无效配置。这一行为在 fields.ts 测试用例 中有完整覆盖包括nullable: itemsAndList的arrayWithNullableItemField、嵌套数组nullable: items等场景。字段描述与废弃标注description与deprecationReasonField的配置对象还支持两个对 GraphQL schema 有实际意义的属性description为该字段生成 SDL 描述用于文档与 IntrospectiondeprecationReason标记字段已废弃生成deprecated指令。同样ObjectType也支持description定义见 decorators/types.ts 中的DescriptionOptions与DeprecationOptions。完整示例Recipe 与 Rate 的 SDL 生成综合以上所有特性Recipe类的最终形态为ObjectType({ description: The recipe model }) class Recipe { Field(type ID) id: string; Field({ description: The title of the recipe }) title: string; Field(type [Rate]) ratings: Rate[]; Field({ nullable: true }) averageRating?: number; }它会生成如下的 SDLtype Recipe { id: ID! title: String! ratings: [Rate!]! averageRating: Float }同样地Rate类型类ObjectType() class Rate { Field(type Int) value: number; Field() date: Date; user: User; }生成的 SDL 为type Rate { value: Int! date: Date! }用显式标量覆盖反射类型ID、Int 与 Date在上面的例子中id属性在 TypeScript 中是string但我们通过Field(type ID)把它的 GraphQL 类型覆盖为ID标量value属性是number通过Field(type Int)覆盖为Int否则number默认映射为Float。这是Field的显式类型参数最常见的用途之一——覆盖反射推断出的类型。ID、Int是 TypeGraphQL 提供的标量别名分别对应GraphQLID、GraphQLInt更多标量细节参见 标量文档其中也专门介绍了内置的Date标量默认映射为 ISO 日期时间字符串也可通过scalarsMap换成时间戳格式GraphQLTimestamp。Date→GraphQLISODateTime的映射关系可以在 helpers/types.ts 的convertTypeIfScalar中直接看到。隐藏数据模型属性不加Field()即可注意Rate类中的user属性没有Field()装饰器。这意味着它不会出现在 GraphQL schema 中被完全隐藏。这是一种重要的数据安全实践例如我们需要在数据库中存储Rate.user以防止同一用户重复评分但又不想让该字段对外公开。规则很简单类属性默认不出现在 schema 中只有被Field()标注的才会暴露。这给了你按需选择暴露面、保护内部状态的能力。纯计算字段的优雅处理交给 Field Resolver如果某个对象类型字段是纯计算得来的例如averageRating可以由ratings数组算出且你不想让它污染类的字段签名可以在类中完全省略该属性转而通过字段解析器Field Resolver实现。这样既能保持数据类的纯粹性又能在 GraphQL 层暴露计算字段。具体实现方式见 resolvers 文档。进阶提醒构造器禁令与字段重命名结合当前仓库主分支的 types-and-fields 文档还有两条实战中容易踩坑的规则值得补充禁止定义构造函数TypeGraphQL 在底层会自行实例化对象类型类因此不要在这些类中定义 constructor否则可能干扰实例化流程。名称重写的能力与边界通过ObjectType(ExternalTypeName)可以重命名对外暴露的类型名通过Field({ name: externalFieldName })可以重命名字段名ObjectType(ExternalTypeName) class InternalClassName { Field({ name: externalFieldName }) internalPropertyName: string; }但要注意字段重命名只对输出类型对象类型、接口类型有效对输入类型无效——因为输入字段没有 resolver 能把一个字段值翻译成另一个属性值重命名会导致无法正确映射。小结类型与字段声明的心法类加ObjectType()属性加Field()TypeGraphQL 就能自动生成 schema无需手写 SDL简单类型靠反射自动推断泛型类型数组等必须显式标注推荐Field(type [Rate])函数语法以规避循环依赖默认所有字段非空单个字段用{ nullable: true }加?:列表用nullable: items/itemsAndList精细化控制用type ID、type Int等覆盖反射类型用description/deprecationReason完善 schema 文档不给Field()的属性自动隐藏纯计算字段交给字段解析器保持数据类干净。掌握这些规则后你就能以最少的样板代码构建出类型安全、可自文档化Introspection 友好的 GraphQL schema。更完整的从零搭建流程可继续阅读 bootstrap 指南 与 resolvers 文档。赞分享后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载相关推荐TypeGraphQL 类型与字段指南用 ObjectType 与 Field 从 TypeScript 类生成 GraphQL SchemaTypeGraphQL 类型与字段指南用 ObjectType 与 Field 从 TypeScript 类生成 GraphQL Schema TypeG后端GraphQLAPI设计TypeGraphQL 实战用 ObjectType 与 Field 装饰器从 TypeScript 类生成 GraphQL 对象类型TypeGraphQL 实战用 ObjectType 与 Field 装饰器从 TypeScript 类生成 GraphQL 对象类型 导读 本文围绕 T后端GraphQLAPI设计Graphene ObjectType 完全指南用 Python 类定义 GraphQL 对象类型与 ResolverGraphene ObjectType 完全指南用 Python 类定义 GraphQL 对象类型与 Resolver Graphene 是 Python 生后端API设计上一篇告别手动整理茉莉花插件5分钟搭建Zotero中文文献管理自动化工作流下一篇零基础掌握罗技鼠标宏让你的PUBG压枪更稳定创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。