docs: typescript

This commit is contained in:
James
2021-11-26 17:10:01 -05:00
parent a0fb48c9a3
commit 77a208fff7
12 changed files with 322 additions and 54 deletions

View File

@@ -57,7 +57,7 @@ export interface LocalizedPost {
}[];
id?: string;
blockName?: string;
blockType: "richTextBlock";
blockType: 'richTextBlock';
}[];
}
/**
@@ -73,14 +73,14 @@ export interface BlocksGlobal {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
}
@@ -113,7 +113,7 @@ export interface Admin {
apiKeyIndex?: string;
loginAttempts?: number;
lockUntil?: string;
roles: ("admin" | "editor" | "moderator" | "user" | "viewer")[];
roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[];
publicUser?: (string | PublicUser)[];
}
/**
@@ -126,11 +126,11 @@ export interface AllFields {
descriptionText?: string;
descriptionFunction?: string;
image?: string | Media;
select: "option-1" | "option-2" | "option-3" | "option-4";
selectMany: ("option-1" | "option-2" | "option-3" | "option-4")[];
select: 'option-1' | 'option-2' | 'option-3' | 'option-4';
selectMany: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
dayOnlyDateFieldExample: string;
timeOnlyDateFieldExample?: string;
radioGroupExample: "option-1" | "option-2" | "option-3";
radioGroupExample: 'option-1' | 'option-2' | 'option-3';
email?: string;
number?: number;
group?: {
@@ -149,13 +149,13 @@ export interface AllFields {
testEmail: string;
id?: string;
blockName?: string;
blockType: "email";
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}
| {
author: string | PublicUser;
@@ -163,14 +163,14 @@ export interface AllFields {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
relationship?: string | Conditions;
@@ -178,11 +178,11 @@ export interface AllFields {
relationshipMultipleCollections?:
| {
value: string | LocalizedPost;
relationTo: "localized-posts";
relationTo: 'localized-posts';
}
| {
value: string | Conditions;
relationTo: "conditions";
relationTo: 'conditions';
};
textarea?: string;
richText: {
@@ -258,13 +258,13 @@ export interface Conditions {
testEmail: string;
id?: string;
blockName?: string;
blockType: "email";
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}
| {
author: string | PublicUser;
@@ -272,14 +272,14 @@ export interface Conditions {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
}
@@ -297,13 +297,13 @@ export interface AutoLabel {
testNumber?: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}[];
noLabelBlock?: {
testNumber?: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}[];
items?: {
itemName?: string;
@@ -359,7 +359,7 @@ export interface File {
filename?: string;
mimeType?: string;
filesize?: number;
type: "Type 1" | "Type 2" | "Type 3";
type: 'Type 1' | 'Type 2' | 'Type 3';
owner: string | Admin;
}
/**
@@ -370,9 +370,9 @@ export interface DefaultValueTest {
id: string;
text?: string;
image?: string | Media;
select?: "option-1" | "option-2" | "option-3" | "option-4";
selectMany?: ("option-1" | "option-2" | "option-3" | "option-4")[];
radioGroupExample?: "option-1" | "option-2" | "option-3";
select?: 'option-1' | 'option-2' | 'option-3' | 'option-4';
selectMany?: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
radioGroupExample?: 'option-1' | 'option-2' | 'option-3';
email?: string;
number?: number;
group?: {
@@ -391,13 +391,13 @@ export interface DefaultValueTest {
testEmail: string;
id?: string;
blockName?: string;
blockType: "email";
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}
| {
author: string | PublicUser;
@@ -405,14 +405,14 @@ export interface DefaultValueTest {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
relationship?: string | Conditions;
@@ -420,11 +420,11 @@ export interface DefaultValueTest {
relationshipMultipleCollections?:
| {
value: string | LocalizedPost;
relationTo: "localized-posts";
relationTo: 'localized-posts';
}
| {
value: string | Conditions;
relationTo: "conditions";
relationTo: 'conditions';
};
textarea?: string;
slug?: string;
@@ -444,13 +444,13 @@ export interface Blocks {
testEmail: string;
id?: string;
blockName?: string;
blockType: "email";
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}
| {
author: string | PublicUser;
@@ -458,14 +458,14 @@ export interface Blocks {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
nonLocalizedLayout: (
@@ -473,13 +473,13 @@ export interface Blocks {
testEmail: string;
id?: string;
blockName?: string;
blockType: "email";
blockType: 'email';
}
| {
testNumber: number;
id?: string;
blockName?: string;
blockType: "number";
blockType: 'number';
}
| {
author: string | PublicUser;
@@ -487,14 +487,14 @@ export interface Blocks {
color: string;
id?: string;
blockName?: string;
blockType: "quote";
blockType: 'quote';
}
| {
label: string;
url: string;
id?: string;
blockName?: string;
blockType: "cta";
blockType: 'cta';
}
)[];
}
@@ -577,20 +577,20 @@ export interface RelationshipA {
postLocalizedMultiple?: (
| {
value: string | LocalizedPost;
relationTo: "localized-posts";
relationTo: 'localized-posts';
}
| {
value: string | AllFields;
relationTo: "all-fields";
relationTo: 'all-fields';
}
| {
value: number | CustomID;
relationTo: "custom-id";
relationTo: 'custom-id';
}
)[];
postManyRelationships?: {
value: string | RelationshipB;
relationTo: "relationship-b";
relationTo: 'relationship-b';
};
postMaxDepth?: string | RelationshipB;
customID?: (number | CustomID)[];
@@ -606,20 +606,20 @@ export interface RelationshipB {
postManyRelationships?:
| {
value: string | RelationshipA;
relationTo: "relationship-a";
relationTo: 'relationship-a';
}
| {
value: string | Media;
relationTo: "media";
relationTo: 'media';
};
localizedPosts?: (
| {
value: string | LocalizedPost;
relationTo: "localized-posts";
relationTo: 'localized-posts';
}
| {
value: string | PreviewablePost;
relationTo: "previewable-post";
relationTo: 'previewable-post';
}
)[];
strictAccess?: string | StrictAccess;
@@ -654,10 +654,10 @@ export interface RichText {
*/
export interface Select {
id: string;
Select: "one" | "two" | "three";
SelectHasMany: ("one" | "two" | "three")[];
SelectJustStrings: ("blue" | "green" | "yellow")[];
Radio: "one" | "two" | "three";
Select: 'one' | 'two' | 'three';
SelectHasMany: ('one' | 'two' | 'three')[];
SelectJustStrings: ('blue' | 'green' | 'yellow')[];
Radio: 'one' | 'two' | 'three';
}
/**
* This interface was referenced by `Config`'s JSON-Schema

View File

@@ -106,3 +106,12 @@ const ExampleCollection = {
}
```
### TypeScript
As you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:
```js
import type { Block } from 'payload/types';
```

View File

@@ -239,3 +239,15 @@ This example will display the number of characters allowed as the user types.
}
```
This component will count the number of characters entered.
### TypeScript
You can import the internal Payload `Field` type as well as other common field types as follows:
```js
import type {
Field,
Validate,
Condition,
} from 'payload/types';
```

View File

@@ -313,3 +313,14 @@ Above, you can see that we are creating a custom SlateJS element with a name of
The plugin itself extends Payload's built-in `shouldBreakOutOnEnter` Slate function to add its own element name to the list of elements that should "break out" when the `enter` key is pressed.
### TypeScript
If you are building your own custom Rich Text elements or leaves, you may benefit from importing the following types:
```js
import type {
RichTextCustomElement,
RichTextCustomLeaf,
} from 'payload/types';
```

View File

@@ -188,3 +188,26 @@ const afterLoginHook = async ({
return user;
}
```
## TypeScript
Payload exports a type for each Collection hook which can be accessed as follows:
```js
import type {
CollectionBeforeOperationHook,
CollectionBeforeValidateHook,
CollectionBeforeChangeHook,
CollectionAfterChangeHook,
CollectionAfterReadHook,
CollectionBeforeReadHook,
CollectionBeforeDeleteHook,
CollectionAfterDeleteHook,
CollectionBeforeLoginHook,
CollectionAfterLoginHook,
CollectionAfterForgotPasswordHook,
} from 'payload/types';
// Use hook types here...
}
```

View File

@@ -70,3 +70,31 @@ All field hooks can optionally modify the return value of the field before the o
<strong>Important</strong><br/>
Due to GraphQL's typed nature, you should never change the type of data that you return from a field, otherwise GraphQL will produce errors. If you need to change the shape or type of data, reconsider Field Hooks and instead evaluate if Collection / Global hooks might suit you better.
</Banner>
## TypeScript
Payload exports a type for field hooks which can be accessed and used as follows:
```js
import type { FieldHook } from 'payload/types';
// Field hook type is a generic that takes two arguments:
// 1: The document type
// 2: the value type
type ExampleFieldHook = FieldHook<ExampleDocumentType, string>;
const exampleFieldHook: ExampleFieldHook = (args) => {
const {
value, // Typed as `string` as shown above
data, // Typed as a Partial of your ExampleDocumentType
originalDoc, // Typed as ExampleDocumentType
operation,
req,
}
// Do something here...
return value; // should return a string as typed above, undefined, or null
}
```

View File

@@ -98,3 +98,20 @@ const afterReadHook = async ({
req, // full express request
}) => {...}
```
## TypeScript
Payload exports a type for each Global hook which can be accessed as follows:
```js
import type {
GlobalBeforeValidateHook,
GlobalBeforeChangeHook,
GlobalAfterChangeHook,
GlobalBeforeReadHook,
GlobalAfterReadHook,
} from 'payload/types';
// Use hook types here...
}
```

View File

@@ -0,0 +1,114 @@
---
title: Generating TypeScript Interfaces
label: Generating Types
order: 20
desc: Generate your own TypeScript interfaces based on your collections and globals.
keywords: headless cms, typescript, documentation, Content Management System, cms, headless, javascript, node, react, express
---
While building your own custom functionality into Payload, like plugins, hooks, access control functions, custom routes, GraphQL queries / mutations, or anything else, you may benefit from generating your own TypeScript types dynamically from your Payload config itself.
Run the following command in a Payload project to generate types:
```
payload generate:types
```
You can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly.
For example, let's look at the following simple Payload config:
```ts
const config: Config = {
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
admin: {
user: 'users',
}
collections: [
{
slug: 'users',
fields: [
{
name: 'name',
type: 'text',
required: true,
}
]
},
{
slug: 'posts',
admin: {
useAsTitle: 'title',
},
fields: [
{
name: 'title',
type: 'text',
},
{
name: 'author',
type: 'relationship',
relationTo: 'users',
},
]
}
]
}
```
By generating types, we'll end up with a file containing the following two TypeScript interfaces:
```ts
export interface User {
id: string;
name: string;
email?: string;
resetPasswordToken?: string;
resetPasswordExpiration?: string;
loginAttempts?: number;
lockUntil?: string;
}
export interface Post {
id: string;
title?: string;
author?: string | User;
}
```
#### Customizing the output path of your generated types
You can specify where you want your types to be generated by adding a property to your Payload config:
```
{
// the remainder of your config
typescript: {
outputFile: path.resolve(__dirname, './generated-types.ts'),
},
}
```
The above example places your types next to your Payload config itself as the file `generated-types.ts`. By default, the file will be output to your current working directory as `payload-types.ts`.
#### Adding an NPM script
<Banner type="warning">
<strong>Important:</strong><br/>
Payload needs to be able to find your config to generate your types.
</Banner>
Payload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable. If this applies to you, you can create an NPM script to make generating your types easier.
To add an NPM script to generate your types and show Payload where to find your config, open your `package.json` and update the `scripts` property to the following:
```
{
"scripts": {
"generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
},
}
```
Now you can run `yarn generate:types` to easily generate your types.

View File

@@ -0,0 +1,36 @@
---
title: TypeScript - Overview
label: Overview
order: 10
desc: Payload is the most powerful TypeScript headless CMS available.
keywords: headless cms, typescript, documentation, Content Management System, cms, headless, javascript, node, react, express
---
Payload supports TypeScript natively, and not only that, the entirety of the CMS is built with TypeScript. To get started developing with Payload and TypeScript, you can use one of Payload's built-in boilerplates in one line via `create-payload-app`:
```
npx create-payload-app
```
Pick a TypeScript project type to get started easily.
#### Setting up from Scratch
It's also possible to set up a TypeScript project from scratch. We plan to write up a guide for exactly how—so keep an eye out for that, too.
## Using Payload's Exported Types
Payload exports a number of types that you may find useful while writing your own plugins, hooks, access control functions, custom routes, GraphQL queries / mutations, or anything else.
##### Config Types
- [Base config](/docs/configuration/overview#typescript)
- [Collections](/docs/configuration/collections#typescript)
- [Globals](/docs/configuration/globals#typescript)
- [Fields](/docs/fields/overview#typescript)
##### Hook Types
- [Collection hooks](/docs/hooks/collections#typescript)
- [Global hooks](/docs/hooks/globals#typescript)
- [Field hooks](/docs/hooks/fields#typescript)

View File

@@ -363,6 +363,10 @@ export function generateTypes(): void {
compile(jsonSchema, 'Config', {
unreachableDefinitions: true,
bannerComment: '/* tslint:disable */\n/**\n* This file was automatically generated by Payload CMS.\n* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,\n* and re-run `payload generate:types` to regenerate this file.\n*/',
style: {
singleQuote: true,
},
}).then((compiled) => {
fs.writeFileSync(config.typescript.outputFile, compiled);
payload.logger.info(`Types written to ${config.typescript.outputFile}`);

View File

@@ -14,9 +14,7 @@ export type FieldHookArgs<T extends TypeWithID = any, P = any> = {
req: PayloadRequest
}
export type FieldHookReturnType = Promise<unknown> | unknown;
export type FieldHook<T extends TypeWithID = any, P = any> = (args: FieldHookArgs<T, P>) => FieldHookReturnType;
export type FieldHook<T extends TypeWithID = any, P = any> = (args: FieldHookArgs<T, P>) => Promise<P> | P;
export type FieldAccess<T extends TypeWithID = any, P = any> = (args: {
req: PayloadRequest

20
types.d.ts vendored
View File

@@ -16,5 +16,21 @@ export {
AfterForgotPasswordHook as CollectionAfterForgotPasswordHook,
} from './dist/collections/config/types';
export { GlobalConfig, SanitizedGlobalConfig } from './dist/globals/config/types';
export { Field, FieldHook, FieldAccess, RichTextCustomElement, RichTextCustomLeaf, Block } from './dist/fields/config/types';
export {
GlobalConfig,
SanitizedGlobalConfig,
BeforeValidateHook as GlobalBeforeValidateHook,
BeforeChangeHook as GlobalBeforeChangeHook,
AfterChangeHook as GlobalAfterChangeHook,
BeforeReadHook as GlobalBeforeReadHook,
AfterReadHook as GlobalAfterReadHook,
} from './dist/globals/config/types';
export {
Field,
FieldHook,
FieldAccess,
RichTextCustomElement,
RichTextCustomLeaf,
Block,
} from './dist/fields/config/types';