Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typegen not correctly typing groq query when using dynamic param on an object attribute #6365

Open
oliviercperrier opened this issue Apr 14, 2024 · 5 comments
Labels
Feature typegen Issues related to TypeScript types generation

Comments

@oliviercperrier
Copy link

If you find a security vulnerability, do NOT open an issue. Email security@sanity.io instead.

Describe the bug

i have the following query:

export const postQuery = groq`*[_type == "post" && slug.current == $slug] [0] {
  "body": body[$locale],
  ${postFields}
}`;

*** This is working if i do directly access a locale: body.en

body is LocaleString which normally return

{
  "en": "...",
  "fr": "..."
}

But i pass a locale to only get the currently selected lang in my website, so body is a string. But for some reason, typegen is still generatin this type:

export type PostQueryResult = {
  body: LocaleString | null;
  ...
}

To Reproduce

Steps to reproduce the behavior:

  1. Create LocaleString type
export default defineType({
  title: "Localized string",
  name: "localeString",
  type: "object",
  fieldsets: [
    {
      title: "Translations",
      name: "translations",
      options: { collapsible: true },
    },
  ],
  fields: supportedLanguages.map((lang) => ({
    title: lang.title,
    name: lang.id,
    type: "string",
    fieldset: lang.isDefault ? undefined : "translations",
  })),
});
  1. Defined a field
 defineField({
      name: "title",
      title: "Title",
      type: "localeString",
      validation: (rule) => rule.required(),
    }),
  1. Make a query for a single locale at a type using a dynamic param

Expected behavior

I expect the typegen to generate the following type:

export type PostQueryResult = {
  body: string | null;
  ...
}

Which versions of Sanity are you using?

@sanity/cli (global)          3.37.2 (up to date)
@sanity/eslint-config-studio   4.0.0 (up to date)
@sanity/vision                3.37.2 (up to date)
sanity                        3.37.2 (up to date)

What operating system are you using?

Mac OS

Which versions of Node.js / npm are you running?

10.2.3
v18.19.0

@sgulseth sgulseth added the typegen Issues related to TypeScript types generation label Apr 18, 2024
@linear linear bot added the bug label Apr 24, 2024
@obrassard
Copy link

I have the same issue, this could be very useful for field level translations !

@Qocotzxin
Copy link

Qocotzxin commented May 2, 2024

Similar to this, the following query generates isCurrentLanguage: unknown:
"toasts": toasts[]->{"isCurrentLanguage": language == $language, code, title, content},

While if I use a string literal instead, the type is correctly generated as isCurrentLanguage: string:
"toasts": toasts[]->{"isCurrentLanguage": language == "en", code, title, content},

I also noticed while debugging this query that when using the parent accessor, the type results in Array<never>:
"toasts": *[_type == "toast" && language == $language && _id in ^.toasts[]{_ref}._ref]{code, heading, content, language}

In all these cases queries correctly work and bring the expected results.

@sgulseth sgulseth added Feature and removed bug labels May 2, 2024
@sgulseth
Copy link
Member

sgulseth commented May 2, 2024

Hi! This is is a tricky feature because of the dynamic nature of GROQ. Given the look up attribute[$param] it's impossible to parse the query correctly without knowing the type of $param. If $param is a string it's an attribute access(object), if it's a number it's an element access(array), and by default it's a filter. Since we rely on the GROQ javascript parser we would need to know the value of $param while parsing, which we don't at the moment

@obrassard
Copy link

obrassard commented May 3, 2024

One workaround I tried was to take my initial query with $lang parameters and create a new "localized query" by replacing all references to [$lang] with .en. I thought this would work and then I'd be able to use the type generated for the localized query in the code.

import groq from 'groq';

const fullWidthTextBlockFragment = `
_type == 'module.fullWidthTextBlock' => {
    _key,
    _type,
    "title": title[$lang],
    "body": body[$lang],
}
`;

const SanityProductModulesQuery = groq`
*[_type == 'product' && store.slug.current == $handle][0] {
  modules[] {
    ${fullWidthTextBlockFragment},
  }
}`;

const LocalizedProductModulesQuery = groq`
${SanityProductModulesQuery.replaceAll('[$lang]', '.en')}
`;

⚠️ However it seems the Groq parser does not (yet?) allow those kind of expressions :

✖ Unsupported expression type: MemberExpression in ../app/groq/product.localized.ts:22:4
 ⚠ Encountered errors in 1 files while generating types


  • I think it would be nice to allow membreExpression and other function calls to be used with sanity codegen
  • Even better, it would be awesome if we could pass parameters directly when defining the query (parameters that would be use only for code generation. Something like :
groq(`
*[_type == 'product' && store.slug.current == $handle][0] {
  _type == 'module.fullWidthTextBlock' => {
    "title": title[$lang],
    "body": body[$lang],
  }
}`,
  {
    lang: 'en',
  },
);
  • Else if we know that all fields of a given objet are the same type, for instance body : { fr: string; en: string } I think it would be safe to assume that body[$something] returns a string (or null)

@Qocotzxin
Copy link

It would be nice being able to at least define parameters in JSDoc (or similar) format for this feature work:

/**
* @param language {string}
*/
const someQuery = groq`
*[_type == 'someType' && language == $language] {...}
`;

This approach might not be enough to unblock the usage of typed fragments though, which would be ideal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature typegen Issues related to TypeScript types generation
Projects
None yet
Development

No branches or pull requests

4 participants