Skip to content

Instantly share code, notes, and snippets.

@wesleymoliveira
Created January 20, 2025 22:13
Show Gist options
  • Save wesleymoliveira/af4e8438ffb776f3e5252688ebb40209 to your computer and use it in GitHub Desktop.
Save wesleymoliveira/af4e8438ffb776f3e5252688ebb40209 to your computer and use it in GitHub Desktop.
Strapi v5 types for auto-generated typescipt
import type { Schema, Utils, UID } from '@strapi/strapi';
import { Attribute } from '@strapi/types/dist/schema/attribute';
import { ComponentAttribute, DynamicZoneAttribute, RelationalAttribute } from '@strapi/utils/dist/types';
//https://docs.strapi.io/dev-docs/typescript/development/guides/documents-and-entries
export type ID = `${number}` | number;
export type BooleanValue = boolean | 'true' | 'false' | 't' | 'f' | '1' | '0' | 1 | 0;
export type NumberValue = string | number;
export type DateValue = Schema.Attribute.DateValue | number;
export type TimeValue = Schema.Attribute.TimeValue | number;
export type DateTimeValue = Schema.Attribute.DateTimeValue | number;
export type TimeStampValue = Schema.Attribute.TimestampValue;
export interface MediaAttribute extends Attribute {
type: 'media';
allowedTypes?: string[];
multiple?: boolean;
required?: boolean;
}
export interface StrapiBaseAttributes {
documentId: string;
createdAt: DateTimeValue;
updatedAt: DateTimeValue;
publishedAt: DateTimeValue | null;
}
type IDProperty = { id: number };
type OptionalKeys<T> = {
[K in keyof T]: {} extends Pick<T, K> ? K : never;
}[keyof T];
type AttributeValue<T = unknown> =
| string
| number
| boolean
| null
| undefined
| T
| T[];
type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>;
// identify invalid keys
type InvalidKeys<TSchemaUID extends UID.Schema> = {
[K in keyof Schema.Attributes<TSchemaUID>]:
Schema.Attributes<TSchemaUID>[K] extends { private: true }
? K
: Schema.Attributes<TSchemaUID>[K] extends { type: 'password' }
? K
: never
}[keyof Schema.Attributes<TSchemaUID>];
// Relations
type RelationValue<TAttribute extends RelationalAttribute> =
TAttribute['relation'] extends `${string}ToMany`
? StrapiAPIResponseCOllection<Extract<TAttribute['target'], UID.ContentType>>
: APIResponse<Extract<TAttribute['target'], UID.ContentType>> | null;
/// Components
type ComponentValue<TAttribute extends ComponentAttribute> =
IDProperty &
(TAttribute['repeatable'] extends true
? GetValues<Extract<TAttribute['component'], UID.Component>>[]
: GetValues<Extract<TAttribute['component'], UID.Component>> | null);
// Dynamic Zones
type DynamicZoneValue<TAttribute extends DynamicZoneAttribute> = Array<
IDProperty & {
__component: Extract<TAttribute['components'][number], UID.Component>;
} & GetValues<Extract<TAttribute['components'][number], UID.Component>>
>;
// Media
type MediaValue<TAttribute extends MediaAttribute> =
TAttribute['multiple'] extends true
? StrapiAPIResponseCOllection<'plugin::upload.file'>
: APIResponse<'plugin::upload.file'> | null;
/**
* Get value type based on attribute type
*/
export type GetValue<TAttribute extends Attribute> =
TAttribute extends RelationalAttribute
? RelationValue<TAttribute>
: TAttribute extends DynamicZoneAttribute
? DynamicZoneValue<TAttribute>
: TAttribute extends ComponentAttribute
? ComponentValue<TAttribute>
: TAttribute extends MediaAttribute
? MediaValue<TAttribute>
: AttributeValue<TAttribute>;
// TEST
// declare const getValue: GetValue<{ type: 'relation'; relation: 'manyToOne'; target: 'api::post.post' }>;
// getValue; // Resolves to a single or collection response based on the relation type
/**
* Extract values from schema attributes
*/
export type GetValues<TSchemaUID extends UID.Schema> = Omit<
{
[TKey in OptionalKeys<Schema.Attributes<TSchemaUID>>]?: Schema.Attributes<TSchemaUID>[TKey] extends infer TAttribute
? TAttribute extends Attribute
? GetValue<TAttribute>
: never
: never;
} & {
[TKey in RequiredKeys<Schema.Attributes<TSchemaUID>>]-?: Schema.Attributes<TSchemaUID>[TKey] extends infer TAttribute
? TAttribute extends Attribute
? GetValue<TAttribute>
: never
: never;
},
InvalidKeys<TSchemaUID>
>;
// ------------------------------------------------------------
// API response types
// ------------------------------------------------------------
export interface APIResponseData<TContentTypeUID extends UID.ContentType> extends IDProperty {
attributes: GetValues<TContentTypeUID>;
}
// Metadata
export interface StrapiAPIResponseCollectionMetadata {
pagination: {
page: number;
pageSize: number;
pageCount: number;
total: number;
};
}
// Data for a single API response item
export interface APIResponse<TContentTypeUID extends UID.ContentType> {
data: APIResponseData<TContentTypeUID>;
}
// API response for a collection of items
export interface StrapiAPIResponseCOllection<TContentTypeUID extends UID.ContentType> {
data: APIResponseData<TContentTypeUID>[];
meta: StrapiAPIResponseCollectionMetadata;
}
// TEST
// declare function fetchAPI<T extends UID.ContentType>(uid: T): Promise<APIResponse<T>>;
// fetchAPI('api::user.user').then(response => response.data.attributes);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment