r3plica
r3plica

Reputation: 13367

Typescript and generic functions

hopefully this is a simple question. I have a model that I created called Page that looks like this:

export class Page implements Metadata {
  title: string;
  description: string;
  image: Image;

  slug: string;
  linkText: string;
  elements: Element[];
}

Then I have a method that gets a page and transforms it like this:

public getPage(slug: string): Observable<Page> {
  return from(
    this.client
      .getEntries({
        content_type: 'page',
        'fields.path[match]': slug,
        include: 3,
      })
      .then((response: any) =>
        {
          let page = response.find((item: any) => item.path === slug);
          if (!page) return;

          return this.createPage(page);
        }

      )
  );
}

public createPage(page: Entry<any>): Page {
  return {
    title: page.fields.title,
    description: page.fields.description,
    image: this.createImage(page.fields.image),
    slug: page.fields.path,
    linkText: page.fields.linkText,
    elements: page.fields.content?.map((content: any) =>
      this.createElement(content)
    ),
  };
}

Now I have a new type of page:

export class BlogPost extends Page {
  tags: any[];
  author: string;
  dateCreated: string;
}

So I want to change my method:

public getPage<T extends Page>(slug: string, callback: (page: any) => T = this.createPage(page)): Observable<T> {
  return from(
    this.client
      .getEntries({
        content_type: 'page',
        'fields.path[match]': slug,
        include: 3,
      })
      .then((response: any) =>
        {
          let page = response.find((item: any) => item.path === slug);
          if (!page) return;

          return callback(page);
        }

      )
  );
}

The problem here is that typescript complains:

type 'Page' is not assignable to type '(page: any) => T'. Type 'Page' provides no match for the signature '(page: any): T'.

I figured it must by my factory method, so I change that to this:

public createPage<T extends Page>(page: Entry<any>): T {
  return {
    title: page.fields.title,
    description: page.fields.description,
    image: this.createImage(page.fields.image),
    slug: page.fields.path,
    linkText: page.fields.linkText,
    elements: page.fields.content?.map((content: any) =>
      this.createElement(content)
    ),
  };
}

and that says:

Type '{ title: any; description: any; image: Image; slug: any; linkText: any; elements: any; }' is not assignable to type 'T'. '{ title: any; description: any; image: Image; slug: any; linkText: any; elements: any; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Page'.

Does anyone know what I am doing wrong?

Upvotes: 0

Views: 36

Answers (1)

Tomasz Gawel
Tomasz Gawel

Reputation: 8520

Change signature to:

public getPage<T extends Page>(slug: string, callback: (page: Entry<any>) => T = this.createPage.bind(this)): Observable<T> {

Upvotes: 1

Related Questions