Skip to content

Commit f09ee0b

Browse files
r1tsuumrazauskas
andauthored
ci: add types testing with tstyche (#9803)
As proposed here #9782 (comment) with additional testing of our types we can be more sure that we don't break them between updates. This PR already adds types testing for most Local API methods https://github.com/payloadcms/payload/blob/6beb921c2e232ab4edfa38c480af40a1bec1106e/test/types/types.spec.ts but new tests for types can be easily added, either to that same file or you can create `types.spec.ts` in any other test folder. The new test folder uses `strict: true` to ensure our types do not break with it. --------- Co-authored-by: Tom Mrazauskas <tom@mrazauskas.de>
1 parent 1fdc7cc commit f09ee0b

File tree

11 files changed

+452
-0
lines changed

11 files changed

+452
-0
lines changed

.github/workflows/main.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,33 @@ jobs:
134134
env:
135135
NODE_OPTIONS: --max-old-space-size=8096
136136

137+
tests-types:
138+
runs-on: ubuntu-24.04
139+
needs: [changes, build]
140+
if: ${{ needs.changes.outputs.needs_tests == 'true' }}
141+
steps:
142+
- uses: actions/checkout@v4
143+
144+
- name: Node setup
145+
uses: ./.github/actions/setup
146+
with:
147+
node-version: ${{ env.NODE_VERSION }}
148+
pnpm-version: ${{ env.PNPM_VERSION }}
149+
pnpm-run-install: false
150+
pnpm-restore-cache: false # Full build is restored below
151+
pnpm-install-cache-key: pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
152+
153+
- name: Restore build
154+
uses: actions/cache@v4
155+
with:
156+
path: ./*
157+
key: ${{ github.sha }}-${{ github.run_number }}
158+
159+
- name: Types Tests
160+
run: pnpm test:types --target '>=5.7'
161+
env:
162+
NODE_OPTIONS: --max-old-space-size=8096
163+
137164
tests-int:
138165
runs-on: ubuntu-24.04
139166
needs: [changes, build]

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"test:int": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
9292
"test:int:postgres": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
9393
"test:int:sqlite": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=sqlite DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
94+
"test:types": "tstyche",
9495
"test:unit": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=jest.config.js --runInBand",
9596
"translateNewKeys": "pnpm --filter payload run translateNewKeys"
9697
},
@@ -165,6 +166,7 @@
165166
"sort-package-json": "^2.10.0",
166167
"swc-plugin-transform-remove-imports": "2.0.0",
167168
"tempy": "1.0.1",
169+
"tstyche": "^3.1.1",
168170
"tsx": "4.19.2",
169171
"turbo": "^2.1.3",
170172
"typescript": "5.7.2"

pnpm-lock.yaml

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/types/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/media
2+
/media-gif

test/types/config.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { lexicalEditor } from '@payloadcms/richtext-lexical'
2+
import { fileURLToPath } from 'node:url'
3+
import path from 'path'
4+
5+
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
6+
7+
const filename = fileURLToPath(import.meta.url)
8+
const dirname = path.dirname(filename)
9+
10+
export default buildConfigWithDefaults({
11+
// ...extend config here
12+
collections: [
13+
{
14+
slug: 'posts',
15+
versions: true,
16+
fields: [
17+
{
18+
type: 'text',
19+
name: 'text',
20+
},
21+
],
22+
},
23+
],
24+
admin: {
25+
importMap: {
26+
baseDir: path.resolve(dirname),
27+
},
28+
},
29+
editor: lexicalEditor({}),
30+
globals: [
31+
{
32+
slug: 'menu',
33+
versions: true,
34+
fields: [
35+
{
36+
type: 'text',
37+
name: 'text',
38+
},
39+
],
40+
},
41+
],
42+
typescript: {
43+
outputFile: path.resolve(dirname, 'payload-types.ts'),
44+
},
45+
})

test/types/eslint.config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { rootParserOptions } from '../../eslint.config.js'
2+
import { testEslintConfig } from '../eslint.config.js'
3+
4+
/** @typedef {import('eslint').Linter.Config} Config */
5+
6+
/** @type {Config[]} */
7+
export const index = [
8+
...testEslintConfig,
9+
{
10+
languageOptions: {
11+
parserOptions: {
12+
...rootParserOptions,
13+
tsconfigRootDir: import.meta.dirname,
14+
},
15+
},
16+
},
17+
]
18+
19+
export default index

test/types/payload-types.ts

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
/**
4+
* This file was automatically generated by Payload.
5+
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
6+
* and re-run `payload generate:types` to regenerate this file.
7+
*/
8+
9+
export interface Config {
10+
auth: {
11+
users: UserAuthOperations;
12+
};
13+
collections: {
14+
posts: Post;
15+
users: User;
16+
'payload-locked-documents': PayloadLockedDocument;
17+
'payload-preferences': PayloadPreference;
18+
'payload-migrations': PayloadMigration;
19+
};
20+
collectionsJoins: {};
21+
collectionsSelect: {
22+
posts: PostsSelect<false> | PostsSelect<true>;
23+
users: UsersSelect<false> | UsersSelect<true>;
24+
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
25+
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
26+
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
27+
};
28+
db: {
29+
defaultIDType: string;
30+
};
31+
globals: {
32+
menu: Menu;
33+
};
34+
globalsSelect: {
35+
menu: MenuSelect<false> | MenuSelect<true>;
36+
};
37+
locale: null;
38+
user: User & {
39+
collection: 'users';
40+
};
41+
jobs: {
42+
tasks: unknown;
43+
workflows: unknown;
44+
};
45+
}
46+
export interface UserAuthOperations {
47+
forgotPassword: {
48+
email: string;
49+
password: string;
50+
};
51+
login: {
52+
email: string;
53+
password: string;
54+
};
55+
registerFirstUser: {
56+
email: string;
57+
password: string;
58+
};
59+
unlock: {
60+
email: string;
61+
password: string;
62+
};
63+
}
64+
/**
65+
* This interface was referenced by `Config`'s JSON-Schema
66+
* via the `definition` "posts".
67+
*/
68+
export interface Post {
69+
id: string;
70+
text?: string | null;
71+
updatedAt: string;
72+
createdAt: string;
73+
}
74+
/**
75+
* This interface was referenced by `Config`'s JSON-Schema
76+
* via the `definition` "users".
77+
*/
78+
export interface User {
79+
id: string;
80+
updatedAt: string;
81+
createdAt: string;
82+
email: string;
83+
resetPasswordToken?: string | null;
84+
resetPasswordExpiration?: string | null;
85+
salt?: string | null;
86+
hash?: string | null;
87+
loginAttempts?: number | null;
88+
lockUntil?: string | null;
89+
password?: string | null;
90+
}
91+
/**
92+
* This interface was referenced by `Config`'s JSON-Schema
93+
* via the `definition` "payload-locked-documents".
94+
*/
95+
export interface PayloadLockedDocument {
96+
id: string;
97+
document?:
98+
| ({
99+
relationTo: 'posts';
100+
value: string | Post;
101+
} | null)
102+
| ({
103+
relationTo: 'users';
104+
value: string | User;
105+
} | null);
106+
globalSlug?: string | null;
107+
user: {
108+
relationTo: 'users';
109+
value: string | User;
110+
};
111+
updatedAt: string;
112+
createdAt: string;
113+
}
114+
/**
115+
* This interface was referenced by `Config`'s JSON-Schema
116+
* via the `definition` "payload-preferences".
117+
*/
118+
export interface PayloadPreference {
119+
id: string;
120+
user: {
121+
relationTo: 'users';
122+
value: string | User;
123+
};
124+
key?: string | null;
125+
value?:
126+
| {
127+
[k: string]: unknown;
128+
}
129+
| unknown[]
130+
| string
131+
| number
132+
| boolean
133+
| null;
134+
updatedAt: string;
135+
createdAt: string;
136+
}
137+
/**
138+
* This interface was referenced by `Config`'s JSON-Schema
139+
* via the `definition` "payload-migrations".
140+
*/
141+
export interface PayloadMigration {
142+
id: string;
143+
name?: string | null;
144+
batch?: number | null;
145+
updatedAt: string;
146+
createdAt: string;
147+
}
148+
/**
149+
* This interface was referenced by `Config`'s JSON-Schema
150+
* via the `definition` "posts_select".
151+
*/
152+
export interface PostsSelect<T extends boolean = true> {
153+
text?: T;
154+
updatedAt?: T;
155+
createdAt?: T;
156+
}
157+
/**
158+
* This interface was referenced by `Config`'s JSON-Schema
159+
* via the `definition` "users_select".
160+
*/
161+
export interface UsersSelect<T extends boolean = true> {
162+
updatedAt?: T;
163+
createdAt?: T;
164+
email?: T;
165+
resetPasswordToken?: T;
166+
resetPasswordExpiration?: T;
167+
salt?: T;
168+
hash?: T;
169+
loginAttempts?: T;
170+
lockUntil?: T;
171+
}
172+
/**
173+
* This interface was referenced by `Config`'s JSON-Schema
174+
* via the `definition` "payload-locked-documents_select".
175+
*/
176+
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
177+
document?: T;
178+
globalSlug?: T;
179+
user?: T;
180+
updatedAt?: T;
181+
createdAt?: T;
182+
}
183+
/**
184+
* This interface was referenced by `Config`'s JSON-Schema
185+
* via the `definition` "payload-preferences_select".
186+
*/
187+
export interface PayloadPreferencesSelect<T extends boolean = true> {
188+
user?: T;
189+
key?: T;
190+
value?: T;
191+
updatedAt?: T;
192+
createdAt?: T;
193+
}
194+
/**
195+
* This interface was referenced by `Config`'s JSON-Schema
196+
* via the `definition` "payload-migrations_select".
197+
*/
198+
export interface PayloadMigrationsSelect<T extends boolean = true> {
199+
name?: T;
200+
batch?: T;
201+
updatedAt?: T;
202+
createdAt?: T;
203+
}
204+
/**
205+
* This interface was referenced by `Config`'s JSON-Schema
206+
* via the `definition` "menu".
207+
*/
208+
export interface Menu {
209+
id: string;
210+
text?: string | null;
211+
updatedAt?: string | null;
212+
createdAt?: string | null;
213+
}
214+
/**
215+
* This interface was referenced by `Config`'s JSON-Schema
216+
* via the `definition` "menu_select".
217+
*/
218+
export interface MenuSelect<T extends boolean = true> {
219+
text?: T;
220+
updatedAt?: T;
221+
createdAt?: T;
222+
globalType?: T;
223+
}
224+
/**
225+
* This interface was referenced by `Config`'s JSON-Schema
226+
* via the `definition` "auth".
227+
*/
228+
export interface Auth {
229+
[k: string]: unknown;
230+
}
231+
232+
233+
declare module 'payload' {
234+
// @ts-ignore
235+
export interface GeneratedTypes extends Config {}
236+
}

0 commit comments

Comments
 (0)