prismicio / gatsby-source-prismic-graphql

Gatsby source plugin for Prismic GraphQL
MIT License
17 stars 14 forks source link

Typescript example #12

Closed ohlr closed 4 years ago

ohlr commented 4 years ago

I think a typescript example/section in the docs would be good. For example I am not 100% sure if linkresolver must be written in js or ts works too.

When using withPreview I am seeing the following warnings:

image


(JSX attribute) render?: RenderCallback<any> | undefined
No overload matches this call.
  Overload 1 of 2, '(props: Readonly<StaticQueryProps<any>>): StaticQuery<any>', gave the following error.
    Type 'Function | null' is not assignable to type 'RenderCallback<any> | undefined'.
      Type 'null' is not assignable to type 'RenderCallback<any> | undefined'.
  Overload 2 of 2, '(props: StaticQueryProps<any>, context?: any): StaticQuery<any>', gave the following error.
    Type 'Function | null' is not assignable to type 'RenderCallback<any> | undefined'.
      Type 'null' is not assignable to type 'RenderCallback<any> | undefined'.ts(2769)
index.d.ts(140, 3): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<StaticQuery<any>> & Readonly<StaticQueryProps<any>> & Readonly<{ children?: ReactNode; }>'
index.d.ts(140, 3): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<StaticQuery<any>> & Readonly<StaticQueryProps<any>> & Readonly<{ children?: ReactNode; }>'

Parameter 'data' implicitly has an 'any' type.ts(7006)

MarcMcIntosh commented 4 years ago

Interesting. Would you like to add an example of the error to the examples directory?

matthewtgilbride commented 4 years ago

Hello @ohlr @MarcMcIntosh ,

I have seen this as well. FWIW, I have a reference project for my client working with Typescript. The problem above is that the type declaration for withPreview needs refining, because the render prop of the StaticQuery HOC expects a RenderCallback<T>, which expands to (data: T) => React.ReactNode.

We need withPreview to look more like this (not exact, but you should get the idea):

export declare const withPreview = <T>(render: Function, query: any, fragments?: any) => (data: T) => React.ReactNode

For now, I have gotten around this by casting the result of withPreview as follows:

<StaticQuery
      query={query}
      render={withPreview((data: YourInterfaceGoesHere) => { ... }, query) as StaticQueryProps<YourInterfaceGoesHere>['render']}
/>
matthewtgilbride commented 4 years ago

@MarcMcIntosh I can create a PR to correct this type definition, just can't work out why I can't create branches on the repo (see https://github.com/prismicio/gatsby-source-graphql-universal/issues/5#issuecomment-659630938)

matthewtgilbride commented 4 years ago

@MarcMcIntosh @ohlr I failed in my attempt to tweak the withPreview function to play more nicely with RenderCallback<T> from gatsby, which is what the render function of StaticQuery expects. In my own project, I created this shim:

/* eslint-disable @typescript-eslint/no-explicit-any */
import { withPreview } from '@prismicio/gatsby-source-prismic-graphql';
import { StaticQueryProps } from 'gatsby';
import { ReactNode } from 'react';

/**
 * The withPreview helper provided from gatsby-source-prismic-graphql has types that are too wide,
 * and the return type is too wide for the StaticQuery component provided by gatsby.
 *
 * This helper shim does two things:
 *   1. narrows the type of the render argument so we can pass a type generic
 *   2. casts the returned value to the type expected by StaticQuery
 */
export const withPreviewShim = <T>(
  render: (data: T) => ReactNode,
  query: any,
  fragments: any[] = [],
): StaticQueryProps<T>['render'] =>
  withPreview(render, query, fragments) as StaticQueryProps<T>['render'];

I then use it like this:

...

const homepageWithPreview = withPreviewShim<HomepageQuery>(data => { // data is now of type HomePageQuery
  const [prismicContent] = data.prismic.allHomepages.edges ?? [null];
  if (!prismicContent) return null;

  ...
}, homepageQuery);

...

/**
 * Homepage content component
 */
export const Homepage: FC = () => (
  <StaticQuery query={homepageQuery} render={homepageWithPreview} />
);

Happy to assist with a TS example, though if anyone has a few minutes I think it might be more helpful to try and narrow the types of withPreview a bit so it plays more nicely with StaticQuery.

...or should we be using the useStaticQuery hook? This issue on the original repo leads me to believe that is the case (and if so our example should use the hook if possible).

MarcMcIntosh commented 4 years ago

I would use the useStaticQuery hook.... but's that more personal preference. an example would be great, even if it breaks. because at least then we know the error we are trying to fix and an easily reproduce it :)

matthewtgilbride commented 4 years ago

I would use the useStaticQuery hook.... but's that more personal preference. an example would be great, even if it breaks. because at least then we know the error we are trying to fix and an easily reproduce it :)

Just tried and realized I'm actually not sure I know how to do that...and I can't find an example anywhere. Could you point me in the right direction?

I'm confused because useStaticQuery itself returns the data, but the withPreview function expects a function over the data as its argument...

Neither of these, for example, compile (or work):

export const Homepage: FC = () => {
  useStaticQuery<HomepageQuery>(homepageQuery);
  return withPreview(renderHomepage, homepageQuery);
};
export const Homepage: FC = () => {
  useStaticQuery<HomepageQuery>(homepageQuery);
  const Component = withPreview(renderHomepage, homepageQuery);
  return <Component />
};
matthewtgilbride commented 4 years ago

this works:

export const Homepage: FC = () => {
  const data = useStaticQuery(homepageQuery);
  const render = withPreview(renderHomepage, homepageQuery);

  if (!render) return null;

  return render(data);
};

Interestingly, the type of withPreview is correct here...which gives me pause to go much further with my PR #20 . I'll create an example using the hook and then we can discuss StaticQuery later.

matthewtgilbride commented 4 years ago

@ohlr @MarcMcIntosh please see my example in this PR: #24

MarcMcIntosh commented 4 years ago

Merged Thank you :)