Skip to content

Instantly share code, notes, and snippets.

@prevostc
Last active May 18, 2018 23:09
Show Gist options
  • Save prevostc/cb99b1619d50c78dd7e120835784c2f6 to your computer and use it in GitHub Desktop.
Save prevostc/cb99b1619d50c78dd7e120835784c2f6 to your computer and use it in GitHub Desktop.
graphql-binding: automatic types all the way. In response to https://github.com/graphql-binding/graphql-binding/issues/86
import { ApolloError } from "apollo-boost";
import gql from "graphql-tag";
import { graphql } from "react-apollo";
/////////////////////////////////////////////////////////////////////
// somehow generate these interfaces based on the query and schema //
/////////////////////////////////////////////////////////////////////
interface IPost {
id: number;
title: string;
}
interface IResponse {
allPosts?: IPost[];
}
interface IVariables {
first: number;
skip: number;
}
interface IActions {
loadMorePosts: () => void;
}
interface IApolloResponse<R /* The response format */> {
data: R & {
/* the tricky part is that we have to only select some
of the response fields here */
error?: ApolloError;
loading: boolean;
};
};
type ChildProps = IActions & IApolloResponse<IResponse>;
const ALL_POST_QUERY = gql`
query allPosts($first: Int!, $skip: Int!) {
allPosts(first: $first, skip: $skip) {
id
title
}
}
`;
const withData = graphql<{}, IResponse, IVariables, ChildProps>(
ALL_POST_QUERY,
{
options: {
variables: { first: 5, skip: 0 }
},
props: ({ data }): ChildProps => {
return {
data,
loadMorePosts: () => {
// do stuff here
}
};
}
}
);
const component = ({
data: {
loading,
error,
allPosts
},
loadMorePosts
}: ChildProps) => {
if (error) {
return <div>Error loading posts.</div>;
}
if (loading) {
return <div>Loading......</div>;
}
if (allPosts && allPosts.length) {
return (
<div>
<ul>
{allPosts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
<button onClick={() => loadMorePosts()}>Show More</button>
</div>
);
}
return <div>No post to show</div>;
}
export default withData(component)
export const schema = `
type Post {
id, : ID!
title: String!
}
type Query {
allPosts(first: Int!, skip: Int!): [Post]!
}
`;
const resolvers_KO = {
Query: {
// Expected: no compile error.
// Actual:
// ERROR: Parameter '_' implicitely has an 'any' type
// ERROR: Parameter 'args' implicitely has an 'any' type
allPosts: (_, args) => {
// Expected: ERROR: Property 'skkip' does not exists on type 'IArgs'. Did you mean 'skip' ?
// Actual: no compile error.
console.log(args.skkip);
// Expected: ERROR: Property 'id' is missing in type '{}'.
// Actual: no compile error.
return [{}];
}
}
};
///////////////////////////////////////
// somehow generate these interfaces //
///////////////////////////////////////
interface IArgs {
first: number,
skip: number,
}
interface IPost {
id: number,
title: string,
}
interface IAllPosts {
allPosts: (_: never, args: IArgs) => IPost[]
}
interface IResolvers {
Query: IAllPosts
}
// now the compiler does his job properly
const resolvers_OK: IResolvers = {
Query: {
// Expected: no compile error.
// Actual: no compile error.
allPosts: (_, args) => {
// Expected: ERROR: Property 'skkip' does not exists on type 'IArgs'. Did you mean 'skip' ?
// Actual: ERROR: Property 'skkip' does not exists on type 'IArgs'. Did you mean 'skip' ?
console.log(args.skip);
// Expected: ERROR: Property 'id' is missing in type '{}'.
// Actual: ERROR: Property 'id' is missing in type '{}'.
return [{}];
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment