Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

New Feature - Auto-generate TypeScript Interfaces

Published On
New Feature - Auto-generate TypeScript Interfaces
New Feature - Auto-generate TypeScript Interfaces

Payload just shipped a ton of new TypeScript features and improvements. Most notably—if you're using Payload with TypeScript, you can now generate types automatically for all of your collections and globals to use within your app's code.

We've been working hard at making Payload + TypeScript a match made in heaven, and with its newest version (0.13.6), building in TypeScript just got a lot better. Most notably, we've shipped a new command that allows you to generate TypeScript types automatically from all your Global and Collection configs:

1
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 an example of how powerful this is, let's look at the following example config:

1
const config: Config = {
2
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
3
admin: {
4
user: 'users',
5
},
6
collections: [
7
{
8
slug: 'users',
9
fields: [
10
{
11
name: 'name',
12
type: 'text',
13
required: true,
14
}
15
]
16
},
17
{
18
slug: 'posts',
19
admin: {
20
useAsTitle: 'title',
21
},
22
fields: [
23
{
24
name: 'title',
25
type: 'text',
26
},
27
{
28
name: 'author',
29
type: 'relationship',
30
relationTo: 'users',
31
},
32
{
33
name: 'status',
34
type: 'select',
35
required: true,
36
options: ['published', 'draft'],
37
}
38
]
39
}
40
]
41
}

By generating types, we'll end up with a file containing the following two TypeScript interfaces:

1
export interface User {
2
id: string;
3
name: string;
4
email?: string;
5
resetPasswordToken?: string;
6
resetPasswordExpiration?: string;
7
loginAttempts?: number;
8
lockUntil?: string;
9
}
10
11
export interface Post {
12
id: string;
13
title?: string;
14
author?: string | User;
15
status: 'published' | 'draft'
16
}

All of your fields are automatically converted into their TypeScript equivalent properties. Even relationship fields are typed properly and will reference your other collection interfaces accurately.

Customizing the output path of your generated types

Specifying where you want your types to be generated is easy. Just add a property to your Payload config:

1
{
2
// the remainder of your config
3
typescript: {
4
outputFile: path.resolve(__dirname, './generated-types.ts'),
5
},
6
}

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.

Using generics in your Payload app functions

By providing the types to your hook, access control and validation functions your code editor can now give you typeahead suggestions. Writing code with autocompletion from TypeScript vastly improves the developer experience.

To demonstrate some of the new TypeScript functionality that Payload offers, let's build a way to automatically set the author of a Posts collection to the currently logged-in user if no author is manually set.

Here is an example of a Field hook for the author field of the above Posts collection:

1
import { FieldHook } from 'payload/types';
2
import { Post, User } from '../generated-types';
3
4
// The `FieldHook` type is a generic that accepts two types:
5
6
// 1. The type of the collection or global that the field is assigned to,
7
// which is used to type the `originalDoc` and `data`
8
// 2. The type of the value that the field itself stores
9
// which will be used to type the `value` arg
10
11
const populateAuthor: FieldHook<Post, Post['author']> = (args) => {
12
const {
13
value, // this is typed to the Author field of our Post collection automatically
14
originalDoc, // This is typed to an entire Post
15
data, // This is a Partial<Post>
16
operation,
17
req,
18
} = args;
19
20
// If there is no value set manually
21
// and only in the `create` operation
22
if (operation === 'create' && !value) {
23
return req.user.id;
24
}
25
26
// Otherwise just return whatever the incoming value is
27
return value;
28
}

There is a lot of power with Payload's new functionality, and we hope you're excited! We'd also love to know what you think. Stop by our GitHub Discussions board to join in on the conversations and help shape the future of Payload.

Documentation

Complete information on generating types can be viewed in our documentation.

More on the way

In addition to the TypeScript updates and improvements we've mentioned in this post, our Roadmap features a few more features and updates that will further solidify Payload as the most advanced and feature-packed headless CMS available.

Next up: RevisionsAutosaveDrafts, and loading UI improvements! Keep an eye out!