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

Fields are incorrectly extending Entry #108

Open
asimpson opened this issue Jan 12, 2022 · 7 comments
Open

Fields are incorrectly extending Entry #108

asimpson opened this issue Jan 12, 2022 · 7 comments

Comments

@asimpson
Copy link

My generated type looks something like this:

export interface Link
  extends Entry<LinkFields> {
  sys: {--snip--};
}

LinkFields looks something like this:

export interface LinkFields {
  /** Link Text */
  linkText: string;

  /** Link */
  link: string;
}

and for reference, Entry's type signature is this:

export interface Entry<T> {
    sys: Sys;
    fields: T;
    metadata: Metadata;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

Entry includes toPlainObject and update neither of which are present on fields of an Entry (to the best of my understanding).

My understanding of the "resolved" type signature looks like this:

interface Link {
    sys: {--snip--};
    fields: {
      /** Link Text */
      linkText: string;

      /** Link */
      link: string;
    };
    metadata: Metadata;
    toPlainObject(): object;
    update(): Promise<Entry<T>>;
}

This is incorrect, it should be something like this:

interface Link {
    sys: {--snip--};
    fields: {
      /** Link Text */
      linkText: string;

      /** Link */
      link: string;
    };
    metadata: Metadata;
}
@intcreator
Copy link

intcreator commented Apr 23, 2022

I'm having a similar issue. where IMyContentTypeName incorrectly extends interface Entry<IMyContentTypeNameFields>:

Types of property 'sys' are incompatible.
    Type '{ id: string; type: string; createdAt: string; updatedAt: string; locale: string; contentType: { sys: { id: "primaryNavigation"; linkType: "ContentType"; type: "Link"; }; }; }' is missing the following properties from type 'EntrySys': revision, space, environment

this is what is generated for one of my components:

export interface IMyContentTypeName extends Entry<IMyContentTypeNameFields> {
  sys: {
    id: string;
    type: string;
    createdAt: string;
    updatedAt: string;
    locale: string;
    contentType: {
      sys: {
        id: "myContentTypeName";
        linkType: "ContentType";
        type: "Link";
      };
    };
  };
}

and this is what passes the type checks:

export interface IMyContentTypeName extends Entry<IMyContentTypeNameFields> {
  sys: {
    id: string;
    type: string;
    createdAt: EntryFields.Date;
    updatedAt: EntryFields.Date;
    locale: string;
    contentType: {
      sys: {
        id: "myContentTypeName";
        linkType: "ContentType";
        type: "Link";
      };
    };
    revision: number;
    space: { sys: SpaceLink }
    environment: { sys: EnvironmentLink }
  };
}

I also need to add EntryFields, SpaceLink, and EnvironmentLink to the imports.

I tried downgrading to a previous version of the Contentful repo v10 beta to see if that fixed it but it did not

@GabrielAnca are you guys still using it with this issue? seems like it would break something down the line

@GabrielAnca
Copy link
Contributor

GabrielAnca commented Dec 12, 2022

Hi 👋🏻 Thanks a lot for sharing this with us, and sorry for the late response

Starting from the end, we're still using this repo on our day-to-day indeed.

@asimpson good point 🤔 We never had this issue because we don't use toPlainObject nor update, but I ran a test in some of our code and those methods don't exist. Based on Contentful docs they should exist from version 3 onwards: https://github.com/contentful/contentful.js/blob/cdd57af9a234427e35a3487bec0345bcd53323f3/MIGRATION.md?plain=1#L126-L136. If I'm reading this right, this might be an issue on Contentful side. Can you let me know if my reasoning makes any sense?

@intcreator we are hardcoding the sys interface and we actually shouldn't do that. We only intend to hardcode the content type to help typescript with typing. Your proposed solution makes perfect sense to me. An alternative I was thinking about was actually removing duplication by just extending Contentful's Sys interface. Something like:

export interface ICtaLink extends Entry<ICtaLinkFields> {
  sys: Sys & {
    contentType: {
      sys: {
        id: 'ctaLink'
        linkType: 'ContentType'
        type: 'Link'
      }
    }
  }
}

Would something like this work?

@intcreator
Copy link

@GabrielAnca I believe that would work. if it passes the type check immediately after generation that's pretty much all I need

@sebpowell
Copy link

sebpowell commented Jun 5, 2023

I've been experiencing, what I think is a related issue – I'm using version 3.4.0 of this package, and version 9.3.3 of contentful's SDK (v10 doesn't work with the packages generated by this).

After I generate the types, I get something like this:

export interface ICategory extends Entry<ICategoryFields> {
  sys: {
    id: string;
    type: string;
    createdAt: string;
    updatedAt: string;
    locale: string;
    contentType: {
      sys: {
        id: "category";
        linkType: "ContentType";
        type: "Link";
      };
    };
  };
}

I then create a function to fetch this entry, that looks like this:

export const fetchCategories = async (): Promise<
  EntryCollection<ICategoryFields>
> => {
  return await Contentful.getEntries<ICategory["fields"]>({
    content_type: "category",
  });
};

The issue here is that the type for sys.contentType.sys.id is set to string, and not category.

So, when I try to feed this data into a component like so:

const categories = await fetchCategories();

type IHomeProps = {
 categories: ICategory[];
}

<Home categories={categories.items} />

TS gives me the following error:

Type 'Entry<ICategoryFields>[]' is not assignable to type 'ICategory[]'.
  Type 'Entry<ICategoryFields>' is not assignable to type 'ICategory'.
    The types of 'sys.contentType.sys.id' are incompatible between these types.
      Type 'string' is not assignable to type '"category"'.ts(2322)

I've tried a number of different things, but can't get it to work. Short of casting (which I would rather avoid as it defeats the purpose of TS), does anyone know how to get around this?

@samturrell
Copy link

I've also exhausted all options trying to get this to work in userland. Anybody else had any luck?

@andrewmartin
Copy link

andrewmartin commented Mar 14, 2024

I am not 100% sure if I did this right but I got the typing mostly working by:

  1. Creating a utility type that extends the base entry, and nests the fields on fields key
  2. Extending the sys with the EntitySys that comes from contentful so we have the types.

It fixes the TS, but no idea if these types will actually be returned by API calls.

Before:

// type error, this is from raw generated types
export interface IPage extends Entry<IPageFields> {
  sys: {
    id: string;
    type: "Entry";
    createdAt: string;
    updatedAt: string;
    locale: string;
    contentType: {
      sys: {
        id: "page";
        linkType: "ContentType";
        type: "Link";
      };
    };
  }
}

After:

// grab the EntitySys from contentful
import { EntitySys } from "contentful";

// create a utility type
interface WithFieldsType<Fields> {
  contentTypeId: string;
  fields: Fields;
}

// use the utility type instead on the original fields
export interface IPage extends Entry<WithFieldsType<IPageFields>> {
  sys: {
    id: string;
    type: "Entry";
    createdAt: string;
    updatedAt: string;
    locale: string;
    contentType: {
      sys: {
        id: "page";
        linkType: "ContentType";
        type: "Link";
      };
    };
  } & EntitySys; // extend sys here
}

Types working now.

@andrewmartin
Copy link

Okay, I did one more patch because apparently the Entry from contentful is horrible.

import {
  type Asset,
  type Entry, // this sucks
  type EntrySys,
  type BaseEntry,
} from "contentful";

export interface IPage extends WithFieldsType<IPageFields>, BaseEntry {
  sys: {
    id: string;
    type: "Entry";
    createdAt: string;
    updatedAt: string;
    locale: string;
    contentType: {
      sys: {
        id: "page";
        linkType: "ContentType";
        type: "Link";
      };
    };
  } & EntrySys;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants