docs: update code formatting and minor text adjustments in type system documentation (#20186)

This commit is contained in:
Jean-Sébastien Herbaux 2024-04-23 18:19:37 +02:00 committed by GitHub
parent bdaafbbb3c
commit 01bf5cfbe4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 69 additions and 55 deletions

View File

@ -67,8 +67,8 @@ For instance, a string attribute will resolve to a primitive string in an entity
### Usage
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="public" label="Public" default>
@ -89,22 +89,23 @@ declare const component: Schema.Component;
declare function processAnySchema(schema: Schema.Schema): void;
processAnySchema(schema); // ✅
processAnySchema(schema); // ✅
processAnySchema(contentType); // ✅
processAnySchema(component); // ✅
declare function processContentTypeSchema(schema: Schema.ContentType): void;
processContentTypeSchema(schema); // ✅
processContentTypeSchema(schema); // ✅
processContentTypeSchema(contentType); // ✅
processContentTypeSchema(component); // ❌ Error, a component schema is not assignable to a content-type schema
declare function processComponentSchema(schema: Schema.Component): void;
processComponentSchema(schema); // ✅
processComponentSchema(schema); // ✅
processComponentSchema(contentType); // ❌ Error, a content-type schema is not assignable to a component schema
processComponentSchema(component); // ✅
```
</TabItem>
<TabItem value="internal" label="Internal">
Schema definitions exported from the `Struct` namespace defines the low level type representation of Strapi schemas.
@ -112,6 +113,7 @@ Schema definitions exported from the `Struct` namespace defines the low level ty
:::caution
Those types can be useful when you want to validate other types against the base ones, but realistically, the public Schema types should almost always be preferred.
:::
```typescript
import type { Struct } from '@strapi/strapi';
@ -121,21 +123,22 @@ declare const component: Struct.ComponentSchema;
declare function processAnySchema(schema: Struct.Schema): void;
processAnySchema(schema); // ✅
processAnySchema(schema); // ✅
processAnySchema(contentType); // ✅
processAnySchema(component); // ✅
declare function processContentTypeSchema(schema: Struct.ContentTypeSchema): void;
processContentTypeSchema(schema); // ✅
processContentTypeSchema(schema); // ✅
processContentTypeSchema(contentType); // ✅
processContentTypeSchema(component); // ❌ Error, a component schema is not assignable to a content-type schema
declare function processComponentSchema(schema: Struct.ComponentSchema): void;
processComponentSchema(schema); // ✅
processComponentSchema(schema); // ✅
processComponentSchema(contentType); // ❌ Error, a content-type schema is not assignable to a component schema
processComponentSchema(component); // ✅
```
</TabItem>
</Tabs>

View File

@ -14,7 +14,6 @@ On this page, **a resource** is considered as **anything that can be identified
This includes (but is not limited to) controllers, schema, services, policies, middlewares, etc...
:::
In the Type System, UIDs play a crucial role in referencing various resources (such as schema and entities) by attaching a unique identifier.
To put it simply, a UID is a unique (string) literal key used to identify, locate, or access a particular resource within the system.
@ -26,6 +25,7 @@ This makes it the perfect tool to index type registries or to use as a type para
### Format
A UID is composed of 3 different parts:
1. A namespace ([link](#1-namespaces))
2. A separator ([link](#2-separators))
3. A name ([link](#3-names))
@ -46,7 +46,7 @@ Scoped namespaces are defined by a base name, followed by a separator (`::`) and
In Strapi there are two of them:
| Name | Definition | Description |
|--------|:-----------------:|------------------------------------------------------|
| ------ | :---------------: | ---------------------------------------------------- |
| API | `api::<scope>` | Represent a resource present in the `<scope>` API |
| Plugin | `plugin::<scope>` | Represent a resource present in the `<scope>` plugin |
@ -57,7 +57,7 @@ These namespaces are used as a simple prefix and define the origin of a resource
Strapi uses three of them to create UIDs
| Name | Definition | Description |
|--------|:----------:|-------------------------------------------------------------------------------|
| ------ | :--------: | ----------------------------------------------------------------------------- |
| Strapi | `strapi` | Represent a resource present in the core of strapi |
| Admin | `admin` | Represent a resource present in Strapi admin |
| Global | `global` | Rarely used (_e.g. policies or middlewares_), it represents a global resource |
@ -90,7 +90,7 @@ ContentType and Component are referring to both the related schema and entity.
:::
| | ContentType | Component | Middleware | Policy | Controller | Service |
|--------------------------|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:|
| ------------------------ | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: | :----------------: |
| `api::<scope>.<name>` | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `plugin::<scope>.<name>` | :white_check_mark: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `<category>.<name>` | :x: | :white_check_mark: | :x: | :x: | :x: | :x: |
@ -141,6 +141,7 @@ fetch('api::article.article');
fetch('admin::user');
// ^ this should return a Data.Entity<'admin::user'>
```
To do that, we'll need the function to be able to provide us with the current `uid` type based on usage.
```typescript
@ -171,14 +172,15 @@ Let's add the possibility to select which fields we want to return for our entit
```typescript
import type { UID, Data, Schema } from '@strapi/types';
declare function fetch<
T extends UID.ContentType,
F extends Schema.AttributeNames<T>
>(uid: T, fields: F[]): Data.ContentType<T>;
declare function fetch<T extends UID.ContentType, F extends Schema.AttributeNames<T>>(
uid: T,
fields: F[]
): Data.ContentType<T>;
```
:::tip
You may have noticed that we're using the inferred UID type (`T`) to reference both:
- An entity (`Data.Entity<T>`)
- A schema (`Schema.AttributeNames<T>`)

View File

@ -9,8 +9,8 @@ tags:
toc_max_heading_level: 5
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
### Context
@ -108,6 +108,7 @@ Creating a new registry is as simple as exporting an indexed interface from the
Let's declare the content-type schema registry.
It should accept:
- Content-type UIDs as keys
- Content-type schemas as values
@ -130,7 +131,7 @@ We use low level types to define our index (`Internal`/`Struct`) to keep it as g
To define `UID.ContentType`, we extract every key (`Internal.Registry.Keys`) from the public content-type registry (`Public.ContentTypeSchemas`) that matches with the base definition of a content-type UID (`Internal.UID.ContentType`).
```ts title="@strapi/types/uid.ts"
import type { Internal, Public } from '@strapi/types'
import type { Internal, Public } from '@strapi/types';
export type ContentType = Internal.Registry.Keys<
Public.ContentTypeSchemas,
@ -151,7 +152,7 @@ Since `UID.ContentType` (`TUID`) is [dynamically built based on actual keys](#ui
:::
```ts title="@strapi/types/schema.ts"
import type { UID, Public } from '@strapi/types'
import type { UID, Public } from '@strapi/types';
export type ContentType<TUID extends UID.ContentType> = Public.ContentTypeSchemas[TUID];
```
@ -172,6 +173,7 @@ Remember to use dynamic type definitions (`UID`, `Data`, `Schema`) and not stati
:::info[Reminder]
Registries are **indexed**, which means that:
- **When augmented** (_e.g. in users' applications_), they'll return **strongly typed values** that correspond to the defined types.
- **When empty** (_e.g. in Strapi codebase_), they'll return **generic low level types** based on their index definition.
@ -183,6 +185,7 @@ Registries are **indexed**, which means that:
const uid: UID.ContentType;
// ^ 'api::article.article' | 'admin::user'
```
</TabItem>
<TabItem value="empty" label="Empty">
```ts
@ -191,6 +194,7 @@ Registries are **indexed**, which means that:
const uid: UID.ContentType;
// ^ `admin::${string}` | `api::${string}.${string}` | `plugin::${string}.${string}` | `strapi::${string}`
```
</TabItem>
</Tabs>
:::
@ -215,7 +219,7 @@ declare module '@strapi/strapi' {
}
}
}
````
```
This will force every type that depends on the `Public.ContentTypeSchemas` registry to recognize `'api::article.article'` as the only valid UID and `ApiArticleArticle` the only valid schema.
@ -235,18 +239,20 @@ The process will generate type definitions based on the user application state (
<TabItem value="manual" label="Manual Run">
Generate the types once.
```shell title="my-app/"
yarn strapi ts:generate-types
```
```shell title="my-app/"
yarn strapi ts:generate-types
```
</TabItem>
<TabItem value="dev" label="During Development">
Start the application in dev mode, and generate types on every server restart.
Useful when working with the content-type builder.
```shell title="my-app/"
yarn develop
```
```shell title="my-app/"
yarn develop
```
</TabItem>
</Tabs>
@ -277,40 +283,43 @@ declare module '@strapi/strapi' {
When coupling everything together, the end result is a TypeScript developer experience automatically adjusted to the current context.
<!-- prettier-ignore-start -->
<Tabs>
<TabItem value="app" label="User Application">
```ts title="my-app/src/index.ts"
export default () => ({
bootstrap() {
strapi.findOne('ap');
// ^ TypeScript will autocomplete with "api::article.article"
<TabItem value="app" label="User Application">
```ts title="my-app/src/index.ts"
export default () => ({
bootstrap() {
strapi.findOne('ap');
// ^ TypeScript will autocomplete with "api::article.article"
strapi.findOne('ad');
// ^ TypeScript will autocomplete with "admin::user"
strapi.findOne('ad');
// ^ TypeScript will autocomplete with "admin::user"
strapi.findOne('api::blog.blog');
// ^ Error, TypeScript will complain
}
})
```
</TabItem>
<TabItem value="strapi" label="Strapi Codebase">
```ts title="@strapi/strapi/document-service.ts"
import type { UID } from '@strapi/types';
export const findOne<TUID extends UID.ContentType>(uid: TUID) {
// ...
strapi.findOne('api::blog.blog');
// ^ Error, TypeScript will complain
}
})
```
</TabItem>
<TabItem value="strapi" label="Strapi Codebase">
```ts title="@strapi/strapi/document-service.ts"
import type { UID } from '@strapi/types';
findOne('admin::foo');
// ^ Valid, matches 'admin::${string}'
export const findOne<TUID extends UID.ContentType>(uid: TUID) {
// ...
}
findOne('plugin::bar.bar');
// ^ Valid, matches 'plugin::${string}.${string}'
findOne('admin::foo');
// ^ Valid, matches 'admin::${string}'
findOne('baz');
// ^ Error, does not correspond to any content-type UID format
```
</TabItem>
findOne('plugin::bar.bar');
// ^ Valid, matches 'plugin::${string}.${string}'
findOne('baz');
// ^ Error, does not correspond to any content-type UID format
```
</TabItem>
</Tabs>
<!-- prettier-ignore-end -->