如何使用带有TypeScript的GraphQL和从graphql-code-generator生成的类型?

rfgamaral

我正在遵循Apollo Docs教程来使用TypeScript构建Apollo Server(Express),并且我还使用GraphQL代码生成器根据我的GraphQL模式生成必要的类型。

这是我当前的codegen.json配置:

{
  "schema": "./lib/schema/index.graphql",
  "generates": {
    "./dist/typings/graphql/schema.d.ts": {
      "plugins": [
        "typescript",
        "typescript-resolvers"
      ],
      "config": {
        "typesPrefix": "GQL",
        "skipTypename": true,
        "noSchemaStitching": true,
        "useIndexSignature": true
      }
    }
  }
}

这是基于教程的我当前的GraphQL模式(尚不完整,我还没有完成全部操作,因此我做了一些调整以使示例更小):

type Query {
    launch(id: ID!): Launch
}

type Launch {
    id: ID!
    site: String
    mission: Mission
}

enum PatchSize {
    SMALL
    LARGE
}

type Mission {
    name: String
    missionPatch(mission: String, size: PatchSize): String
}

生成以下TypeScript类型:

import { GraphQLResolveInfo } from 'graphql';
export type Maybe<T> = T | null;
export type RequireFields<T, K extends keyof T> = { [X in Exclude<keyof T, K>]?: T[X] } & { [P in K]-?: NonNullable<T[P]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string,
  String: string,
  Boolean: boolean,
  Int: number,
  Float: number,
};

export type GQLLaunch = {
  id: Scalars['ID'],
  site?: Maybe<Scalars['String']>,
  mission?: Maybe<GQLMission>,
};

export type GQLMission = {
  name?: Maybe<Scalars['String']>,
  missionPatch?: Maybe<Scalars['String']>,
};


export type GQLMissionMissionPatchArgs = {
  mission?: Maybe<Scalars['String']>,
  size?: Maybe<GQLPatchSize>
};

export enum GQLPatchSize {
  Small = 'SMALL',
  Large = 'LARGE'
}

export type GQLQuery = {
  launch?: Maybe<GQLLaunch>,
};


export type GQLQueryLaunchArgs = {
  id: Scalars['ID']
};

export type WithIndex<TObject> = TObject & Record<string, any>;
export type ResolversObject<TObject> = WithIndex<TObject>;

export type ResolverTypeWrapper<T> = Promise<T> | T;

export type ResolverFn<TResult, TParent, TContext, TArgs> = (
  parent: TParent,
  args: TArgs,
  context: TContext,
  info: GraphQLResolveInfo
) => Promise<TResult> | TResult;

export type Resolver<TResult, TParent = {}, TContext = {}, TArgs = {}> = ResolverFn<TResult, TParent, TContext, TArgs>;

export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
  parent: TParent,
  args: TArgs,
  context: TContext,
  info: GraphQLResolveInfo
) => AsyncIterator<TResult> | Promise<AsyncIterator<TResult>>;

export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
  parent: TParent,
  args: TArgs,
  context: TContext,
  info: GraphQLResolveInfo
) => TResult | Promise<TResult>;

export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> {
  subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>;
  resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>;
}

export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
  subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>;
  resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
}

export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
  | SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
  | SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;

export type SubscriptionResolver<TResult, TKey extends string, TParent = {}, TContext = {}, TArgs = {}> =
  | ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
  | SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>;

export type TypeResolveFn<TTypes, TParent = {}, TContext = {}> = (
  parent: TParent,
  context: TContext,
  info: GraphQLResolveInfo
) => Maybe<TTypes>;

export type NextResolverFn<T> = () => Promise<T>;

export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs = {}> = (
  next: NextResolverFn<TResult>,
  parent: TParent,
  args: TArgs,
  context: TContext,
  info: GraphQLResolveInfo
) => TResult | Promise<TResult>;

/** Mapping between all available schema types and the resolvers types */
export type GQLResolversTypes = ResolversObject<{
  Query: ResolverTypeWrapper<{}>,
  ID: ResolverTypeWrapper<Scalars['ID']>,
  Launch: ResolverTypeWrapper<GQLLaunch>,
  String: ResolverTypeWrapper<Scalars['String']>,
  Mission: ResolverTypeWrapper<GQLMission>,
  PatchSize: GQLPatchSize,
  Boolean: ResolverTypeWrapper<Scalars['Boolean']>,
}>;

/** Mapping between all available schema types and the resolvers parents */
export type GQLResolversParentTypes = ResolversObject<{
  Query: {},
  ID: Scalars['ID'],
  Launch: GQLLaunch,
  String: Scalars['String'],
  Mission: GQLMission,
  PatchSize: GQLPatchSize,
  Boolean: Scalars['Boolean'],
}>;

export type GQLLaunchResolvers<ContextType = any, ParentType extends GQLResolversParentTypes['Launch'] = GQLResolversParentTypes['Launch']> = ResolversObject<{
  id?: Resolver<GQLResolversTypes['ID'], ParentType, ContextType>,
  site?: Resolver<Maybe<GQLResolversTypes['String']>, ParentType, ContextType>,
  mission?: Resolver<Maybe<GQLResolversTypes['Mission']>, ParentType, ContextType>,
}>;

export type GQLMissionResolvers<ContextType = any, ParentType extends GQLResolversParentTypes['Mission'] = GQLResolversParentTypes['Mission']> = ResolversObject<{
  name?: Resolver<Maybe<GQLResolversTypes['String']>, ParentType, ContextType>,
  missionPatch?: Resolver<Maybe<GQLResolversTypes['String']>, ParentType, ContextType, GQLMissionMissionPatchArgs>,
}>;

export type GQLQueryResolvers<ContextType = any, ParentType extends GQLResolversParentTypes['Query'] = GQLResolversParentTypes['Query']> = ResolversObject<{
  launch?: Resolver<Maybe<GQLResolversTypes['Launch']>, ParentType, ContextType, RequireFields<GQLQueryLaunchArgs, 'id'>>,
}>;

export type GQLResolvers<ContextType = any> = ResolversObject<{
  Launch?: GQLLaunchResolvers<ContextType>,
  Mission?: GQLMissionResolvers<ContextType>,
  Query?: GQLQueryResolvers<ContextType>,
}>;

这是我的resolvers.ts文件:

import { GQLPatchSize } from '@typings/graphql/schema';
import { GQLResolvers } from '@typings/graphql/schema';

const resolvers: GQLResolvers = {
    Query: {
        launch: (_, args, { dataSources }) => {
            return dataSources.launchesAPI.getLaunchById(args);
        },
    },
    Mission: {
        missionPatch: (mission, { size } = { size: GQLPatchSize.Large }) => {
            return size === 'SMALL' ? mission.missionPatchSmall : mission.missionPatchLarge;
        },
    },
};

export { resolvers };

最后,我launches.tsLaunchesAPI课程文件

import { GQLLaunch } from '@typings/graphql/schema';
import { GQLQueryLaunchArgs } from '@typings/graphql/schema';
import { RESTDataSource } from 'apollo-datasource-rest';

const SPACEX_API_ENDPOINT = 'https://api.spacexdata.com/v3/';

class LaunchesAPI extends RESTDataSource {
    constructor() {
        super();

        this.baseURL = SPACEX_API_ENDPOINT;
    }

    async getLaunchById({ id }: GQLQueryLaunchArgs) {
        const response = await this.get('launches', { flight_number: id });
        return this.launchReducer(response[0]);
    }

    launchReducer(launch: any): GQLLaunch {
        return {
            id: String(launch.flight_number) || '0',
            site: launch.launch_site && launch.launch_site.site_name,
            mission: {
                name: launch.mission_name,
                missionPatchSmall: launch.links.mission_patch_small,
                missionPatchLarge: launch.links.mission_patch,
            },
        };
    }
}

export { LaunchesAPI };

现在,因为我要输入launchReducer()with的结果GQLLaunch,所以mission属性类型为GQLMission并且此类型只有两个属性namemissionPatch它没有missionPatchSmallmissionPatchLarge,因此出现此错误:

输入'{name:any; missionPatchSmall:任意;missionPatchLarge:任意;}”不可分配给“ GQLMission”类型。对象文字只能指定已知属性,并且'missionPatchSmall'在'GQLMission'类型中不存在。ts(2339)

resolvers.ts尝试读取文件时,文件中存在类似的错误,mission.missionPatchSmall或者类型mission.missionPatchLarge中的mission对象中不存在错误GQLMission

类型“ GQLMission”上不存在属性“ missionPatchSmall”。ts(2339)

我不确定如何处理,建议?

贾斯汀·特罗斯

你把对性能mission不在的一部分GQLMission,然后键入明确missionGQLMission一般而言,您试图从架构中生成类型,但是解析器的返回类型与架构所指定的类型不匹配。

在大多数情况下,您面临的挑战是由于架构设计中的某些缺陷或解析器实现中的某些黑手党造成的。

因此,您的选择通常是:

  • 放弃为解析器使用模式生成的类型(这是我最不喜欢的选项。
  • 改变你的架构,以配合您的解析器返回类型(解决方案缺乏。
  • 更改您的解析器以匹配您的架构返回类型(解析器不足。
  • 更改您的架构和解析器以返回一些新的共享类型(解决架构缺陷并更新新架构的解析器实现。

假设您打算使用解析器生成的模式生成的类型继续前进,我们可以消除选项1并考虑适用于您情况的最后三个选项。

  1. 使解析器实现正确的类型,并更新架构以使其匹配。这意味着更改GQLMission架构中的类型以匹配解析器的返回类型(包括missionPatchLargemissionPatchSmall属性),并允许客户端直接通过其查询架构来查询一个或两个。
  2. 将您的架构屈服为正确的类型,并更新您的解析器实现以进行匹配。这意味着要摆脱多余的返回属性(missionPatchLargemissionPatchSmall),您目前正在使用这些属性来简化实现,并missionPatch在子missionPatchResolver解析器中重新获取适当的(最好是通过缓存来防止perf命中)。
  3. 重新考虑您missionPatch在架构上的表示形式考虑一下性质missionPatch真的是非此即彼的情况吗?此解决方案将涉及更改大小为和的模式API的形状,missionPatch然后将其镜像到您的解析器实现中。

您将做什么取决于a的性质missionPatch我的猜测是最后三个选项之一在这里有意义。如果两个missionPatch类型实际上是不同的变体,它可能是有意义的改变missionPatchmissionPatches,它返回的数组MissionPatch的对象,其可以通过进行过滤size如果一个是另一个的派生,则将它们分开放置missionPatchmissionPatchSmall通过架构公开字符串可能是最有意义的。

编辑:查看正在使用的api,很明显,这些是可以同时请求的独立值。没有小任务或大任务。这些是同一任务的不同大小的图像。我的方法可能是将这些值直接或在嵌套missionPatch属性中都包含在架构中,例如

export type GQLMission = {
  name?: Maybe<Scalars['String']>,

  smallPatchUrl: String,
  largePatchUrl: String,

  # OR

  patch?: MissionPatch,
};

export type MissionPatch = {
  smallUrl: String,
  largeUrl: String
};

旁注:通过图像本身的值对象类型表示图像是很常见的,该对象类型可以包括不同尺寸图像的url以及图像的详细信息,例如长宽比或本机宽度或高度。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

使用graphql-code-generator生成React / Apollo钩子时,数组类型是否不正确?

来自分类Dev

从带有子字段的猫鼬模式生成 Graphql 类型

来自分类Dev

如何缩小由graphQL代码生成的Typescript类型的字体?

来自分类Dev

为GraphQL输出解析自动生成的typescript-mongodb类型

来自分类Dev

GraphQL:如何查询所有可用类型?

来自分类Dev

使用Pure Code First Hot Chocolate GraphQL的某些属性的日期类型

来自分类Dev

如果我不使用放大,如何从AppSync GraphQL架构生成Typescript定义?

来自分类Dev

如何从GraphQL模式生成GraphQL操作

来自分类Dev

如何让 VS Code 或 Typescript 使用我的 JSDoc 类型

来自分类Dev

使用接口和内联片段时如何解析GraphQL中的正确类型

来自分类Dev

如何使用Prisma和GraphQL创建对象类型的自解析数组

来自分类Dev

如何使用带有Loopback的Relay / GraphQL?

来自分类Dev

如何限制用户不要使用带有React和TypeScript的输入类型文件来选择其他文件类型?

来自分类Dev

如何通过内省获得graphql突变的参数和类型?

来自分类Dev

GraphQL,如何返回字节的类型[]

来自分类Dev

使用Apollo Server时如何生成schema.graphql文件?

来自分类Dev

如何使用ReactJS从GraphQL的Enum类型列出成员

来自分类Dev

如何在带有Visual Studio Code的TypeScript中使用Socket.IO?

来自分类Dev

类型“ generator”的对象没有len()

来自分类Dev

如何扩展反向 POCO Generator 生成的 POCO?

来自分类Dev

使用Generator在Java中生成元组

来自分类Dev

使用Generator函数生成日期

来自分类Dev

如何使用带有Mongoose和ES6 Promise的Graphql中的find解析多个记录

来自分类Dev

Graphql类型-GraphQL如何传递一组对象突变

来自分类Dev

如何使用Redux Toolkit(带有TypeScript)解决类型“ AsyncThunkAction”中缺少“属性”类型的问题?

来自分类Dev

如何使用带有graphql订阅的socket.io?

来自分类Dev

如何使用angular和Typescript从.graphql文件加载查询

来自分类Dev

具有数组类型的Graphql突变

来自分类Dev

带有 angular 和 nodejs 的 GraphQl

Related 相关文章

  1. 1

    使用graphql-code-generator生成React / Apollo钩子时,数组类型是否不正确?

  2. 2

    从带有子字段的猫鼬模式生成 Graphql 类型

  3. 3

    如何缩小由graphQL代码生成的Typescript类型的字体?

  4. 4

    为GraphQL输出解析自动生成的typescript-mongodb类型

  5. 5

    GraphQL:如何查询所有可用类型?

  6. 6

    使用Pure Code First Hot Chocolate GraphQL的某些属性的日期类型

  7. 7

    如果我不使用放大,如何从AppSync GraphQL架构生成Typescript定义?

  8. 8

    如何从GraphQL模式生成GraphQL操作

  9. 9

    如何让 VS Code 或 Typescript 使用我的 JSDoc 类型

  10. 10

    使用接口和内联片段时如何解析GraphQL中的正确类型

  11. 11

    如何使用Prisma和GraphQL创建对象类型的自解析数组

  12. 12

    如何使用带有Loopback的Relay / GraphQL?

  13. 13

    如何限制用户不要使用带有React和TypeScript的输入类型文件来选择其他文件类型?

  14. 14

    如何通过内省获得graphql突变的参数和类型?

  15. 15

    GraphQL,如何返回字节的类型[]

  16. 16

    使用Apollo Server时如何生成schema.graphql文件?

  17. 17

    如何使用ReactJS从GraphQL的Enum类型列出成员

  18. 18

    如何在带有Visual Studio Code的TypeScript中使用Socket.IO?

  19. 19

    类型“ generator”的对象没有len()

  20. 20

    如何扩展反向 POCO Generator 生成的 POCO?

  21. 21

    使用Generator在Java中生成元组

  22. 22

    使用Generator函数生成日期

  23. 23

    如何使用带有Mongoose和ES6 Promise的Graphql中的find解析多个记录

  24. 24

    Graphql类型-GraphQL如何传递一组对象突变

  25. 25

    如何使用Redux Toolkit(带有TypeScript)解决类型“ AsyncThunkAction”中缺少“属性”类型的问题?

  26. 26

    如何使用带有graphql订阅的socket.io?

  27. 27

    如何使用angular和Typescript从.graphql文件加载查询

  28. 28

    具有数组类型的Graphql突变

  29. 29

    带有 angular 和 nodejs 的 GraphQl

热门标签

归档