Typescript Schema generation and usage

Hi!

i recently started to use strapi and am struggleing miserably with typings.

My main question is: How is typescript even supposed to be used in strapi?

E.g. when I generate my types I get something like:

export interface ApiSubscriptionSubscription extends Schema.CollectionType {
  collectionName: 'subscriptions';
  // ... the only thing that interests me:
  attributes: {
    title: Attribute.String & Attribute.Required;
    included: Attribute.Component<'simple-types.bulletpoint', true>;
    stripeId: Attribute.String;
    // ...
  }
}

declare module '@strapi/strapi' {
  export module Shared {
    export interface ContentTypes {
      // ...
      'api::subscription.subscription': ApiSubscriptionSubscription;
    }
  }
}

Than, when I try to use it in my code, Should I import it like this?

import type { Shared } from "@strapi/strapi";

interface Subscription = Shared.ContentTypes["api::subscription.subscription"]["attributes"]

const subs: Subscription[] = await strapi
        .query("api::subscription.subscription")
        .findMany(...);

And how do I deal with types like relations?

E.g. I have to following generated type:

export interface ApiCustomerCustomer extends Schema.CollectionType {
  // ...
  attributes: {
   // ...
    subscription: Attribute.Relation<
      'api::customer.customer',
      'oneToOne',
      'api::subscription.subscription'
    >;
    // ...
  };
}

How do I use this in code?

// previous code:
interface Subscription = Shared.ContentTypes["api::subscription.subscription"]["attributes"]

// ...
if (customer) {
  return {
   // ...
    subscription: {
      id:
        (customer.subscription as any as Subscription)?.stripeId || null,
      active: isSubscriptionActive(
        customer.subscriptionStatus as unknown as StripeSubscriptionStatus,
        customer.subscriptionPeriod
      ),
      period: customer.subscriptionPeriod,
    },
  };
}

Without casting subscription to any and than to Subscription, I get the error:
Property ‘stripeId’ does not exist on type ‘Relation<“api::customer.customer”, “oneToOne”, “api::subscription.subscription”>’.

Other example:

included: Attribute.Component<"simple-types.bulletpoint", true>;

When I want to map over the attribute, e.g.:

included: sub.included.map((inc) => inc.label),

I get Property 'map' does not exist on type 'Component<"simple-types.bulletpoint", true>'.

I am really struggling with this, and would be very happy if someone could help me out here, especially since this all happened after my upgrade from 4.9 to 4.11.3.

System Information
  • Strapi Version: 4.11.3

Ok, so after digging deep into the conde changes of the new Typescript System v1 I found that actually not that much has changed, it is just not documented.
Creating a type this way fixes the issues:

import type { Attribute  } from "@strapi/strapi";
export type Customer = Attribute.GetValues<"api::customer.customer">;

However, is there any solution where it is not necessary to do this?
Like using the entityEngine and it will automatically get the types associated with the UID “api::customer.customer”?

Hi there,

Improving TypeScript support is one of our highest priorities in the Developer Experience team.

We’ve started by laying out foundations (the PR you’ve linked) so that we can then iteratively implement types for Strapi API (so that you don’t have to cast everything).

We’re focusing first on our most used public APIs such as the entity service (the one we’re currently working on) or the core factories (controller, service, router, which is already out). We’ll then move to less-used APIs (such as the DB query API).

We didn’t publish any documentation for the internal types yet so that we can experiment a bit more with them internally before declaring them as stable. Once we do that, we’ll propose in-depth documentation with examples and guidelines about how to leverage them to write strongly typed Strapi applications.

In the meantime, using the experimental Attribute.GetValues<UID> is still the best option you have if you want to take advantage of the newest types. Just keep in mind that there can still be some issues with those.

If you really want to try and use the types for the query API, maybe you can try to override the module declaration & the query() definition with one of your own.

@excooo I face a similar misunderstanding while using TS. Can you share a bit more of your experience on this?

For example, I have blogArticle content. Using the following code:

import type {Attribute} from "@strapi/strapi";

export type Article = Attribute.GetValues<'api::blog-article.blog-article'>;

function doSomething(article: Article) {
  console.log(article.title);
}
  • I have one error: line 3 ‘Type “string” does not satisfy constraint “never”’
  • I don’t have any property autocomplete on my article object

What am I missing? Thanks in advance.

I also tried export type Article = ApiBlogArticleBlogArticle['attributes']; but I cannot work with the generated types (Attribute.String, Attribute.Integer instead of string and number)…

hey nash, I’ve faced the same issue, you need to regenerate types. The new typing system generates types into a different set of files.