Skip to content
This repository has been archived by the owner on May 6, 2023. It is now read-only.

Build Helper for inferring field types #196

Closed
bradymwilliams opened this issue Feb 8, 2023 · 3 comments · Fixed by #216
Closed

Build Helper for inferring field types #196

bradymwilliams opened this issue Feb 8, 2023 · 3 comments · Fixed by #216

Comments

@bradymwilliams
Copy link

I have a few places in my codebase where I'm defining shared fields in an array to map over and alter in the document definitions. When I reference those shared fields in the document definition I get the following type error:

`Type '{ name: string; type: SanityType<{ options: (Omit<StringOptions | undefined, "list"> & { list?: [string | { title: string; value: string; }, ...(string | { title: string; value: string; })[]] | undefined; }) | undefined; ... 6 more ...; placeholder?: string | undefined; }, string, string, string>; }[]' is not assignable to type '[FieldOptions<string, ZodTypeAny, unknown, boolean>, ...FieldOptions<string, ZodTypeAny, unknown, boolean>[]]'.

Source provides no match for required element at position 0 in target.`

Weird thing is if I access a shared field by index on the array no typeerror shows so its only iteration that makes this throw. I even tried exporting the FieldsArray type from but that type requires arguments I'm not sure how to make dynamic. Any help/feedback appreciated.

const shared_fields = [
  {
    name: 'name',
    type: s.string(),
  },
];

// type error
const bar = s.document({
  name: 'bar',
  title: 'bar',
  fields: shared_fields,
});


// no type error
const foo = s.document({
  name: 'foo',
  title: 'foo',
  fields: [shared_fields[0]],
});
@saiichihashimoto
Copy link
Owner

I'll shoot from the hip here about what I think is going on. When it's inline in the s.document, it's typing things as literals more often than not, so the generics can pull out the actual fields. fields isn't just an array of arbitrary fields, it's an array with name: 'foo' with type: 'string', etc.

When you pulled it out into shared_fields, I'm assuming it's not of type { name: 'name', ...}[] but actually { name: string, ... }[]. When s.document gets that, it's unclear what to do. It'll be an object where the fields' names are strings, not specific strings. How would it know what the fields' names are or of what type? Try throwing in some as const on things that could get "generalized". For example, the name: 'name' as const. Does that change things?

If that's the issue, it would be nice to have some kind of exported type from this library you could use to make this a little easier. Until then, make sure the types of your shared_fields don't end up broader than is helpful. The moment it's something along the lines of { name: string; type: any }[], you're not actually restricting anything.

I haven't checked out your issue at all to see if this is the case.

@bradymwilliams
Copy link
Author

Thanks for response 🙏 as const doesn't seem to affect anything. I'm already over my skis some here but something worth mentioning is I first reached for an s.field for the shared_fields items similar to sanity's defineField() experience.

@saiichihashimoto
Copy link
Owner

Yeah, so s.document uses generics with fields and those generics get auto-magically derived to a narrower type when you throw fields directly in, so it can give you a document with the correct shape. When it's in an external array like that, the array becomes a much broader type so, even if it didn't error, wouldn't give you the shape you wanted anyways.

Something like this would work, just so it ends up inferring the same type that document would:

const sharedFields = <
  Names extends string,
  Zods extends z.ZodTypeAny,
  ResolvedValues,
  Optionals extends boolean,
  FieldsArray extends TupleOfLength<
    FieldOptions<Names, Zods, ResolvedValues, Optionals>,
    1
  >
>(
  fields: FieldsArray
) => fields;

const fields = sharedFields([
  {
    name: "foo",
    type: boolean(),
  },
  {
    name: "bar",
    optional: true,
    type: string(),
  },
]);

const type = document({
  name: "foo",
  fields,
});

Something I could export from the package, just haven't yet.

@saiichihashimoto saiichihashimoto changed the title Typeerrors when defining fields outside of s.document function Build Helper for inferring field types Feb 24, 2023
saiichihashimoto added a commit that referenced this issue Apr 18, 2023
@kodiakhq kodiakhq bot closed this as completed in #216 Apr 18, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants