Skip to content

chore: migrate to TypeScript strict in Payload package - #4/4 #12733

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 9, 2025
Merged
6 changes: 2 additions & 4 deletions packages/payload/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"build": "rimraf .dist && rimraf tsconfig.tsbuildinfo && pnpm copyfiles && pnpm build:types && pnpm build:swc && pnpm build:esbuild",
"build:esbuild": "echo skipping esbuild",
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
"build:types": "concurrently --group \"tsc --emitDeclarationOnly --outDir dist\" \"tsc-strict\"",
"build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rimraf -g {dist,*.tsbuildinfo}",
"clean:cache": "rimraf node_modules/.cache",
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
Expand Down Expand Up @@ -124,15 +124,13 @@
"@types/pluralize": "0.0.33",
"@types/uuid": "10.0.0",
"@types/ws": "^8.5.10",
"concurrently": "9.1.2",
"copyfiles": "2.4.1",
"cross-env": "7.0.3",
"esbuild": "0.25.5",
"graphql-http": "^1.22.0",
"react-datepicker": "7.6.0",
"rimraf": "6.0.1",
"sharp": "0.32.6",
"typescript-strict-plugin": "2.4.4"
"sharp": "0.32.6"
},
"peerDependencies": {
"graphql": "^16.8.1"
Expand Down
9 changes: 6 additions & 3 deletions packages/payload/src/auth/crypto.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @ts-strict-ignore
import crypto from 'crypto'

const algorithm = 'aes-256-ctr'

export function encrypt(text: string): string {
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(algorithm, this.secret, iv)
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const secret = this.secret
const cipher = crypto.createCipheriv(algorithm, secret, iv)

const encrypted = Buffer.concat([cipher.update(text), cipher.final()])

Expand All @@ -19,7 +20,9 @@ export function decrypt(hash: string): string {
const iv = hash.slice(0, 32)
const content = hash.slice(32)

const decipher = crypto.createDecipheriv(algorithm, this.secret, Buffer.from(iv, 'hex'))
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
const secret = this.secret
const decipher = crypto.createDecipheriv(algorithm, secret, Buffer.from(iv, 'hex'))

const decrypted = Buffer.concat([decipher.update(Buffer.from(content, 'hex')), decipher.final()])

Expand Down
5 changes: 2 additions & 3 deletions packages/payload/src/auth/operations/unlock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { status as httpStatus } from 'http-status'

import type {
Expand Down Expand Up @@ -94,7 +93,7 @@ export const unlockOperation = async <TSlug extends CollectionSlug>(
where: whereConstraint,
})

let result
let result: boolean | null = null

if (user) {
await resetLoginAttempts({
Expand All @@ -112,7 +111,7 @@ export const unlockOperation = async <TSlug extends CollectionSlug>(
await commitTransaction(req)
}

return result
return result!
} catch (error: unknown) {
await killTransaction(req)
throw error
Expand Down
1 change: 1 addition & 0 deletions packages/payload/src/auth/strategies/local/authenticate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-strict-ignore
import crypto from 'crypto'
// @ts-expect-error - no types available
import scmp from 'scmp'

import type { TypeWithID } from '../../../collections/config/types.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/payload/src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export const bin = async () => {
}

const userBinScript = Array.isArray(config.bin)
? config.bin.find(({ key }) => key === script)
? config.bin.find(({ key }: { key: string }) => key === script)
: false

if (userBinScript) {
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/bin/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { ParsedArgs } from 'minimist'

import type { SanitizedConfig } from '../config/types.js'
Expand Down Expand Up @@ -97,7 +96,8 @@ export const migrate = async ({ config, parsedArgs }: Args): Promise<void> => {
skipEmpty,
})
} catch (err) {
throw new Error(`Error creating migration: ${err.message}`)
const error = err instanceof Error ? err.message : 'Unknown error'
throw new Error(`Error creating migration: ${error}`)
}
break
case 'migrate:down':
Expand Down
17 changes: 9 additions & 8 deletions packages/payload/src/collections/config/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// @ts-strict-ignore
import type { I18nClient } from '@payloadcms/translations'
import type { I18nClient, TFunction } from '@payloadcms/translations'

import type { StaticDescription } from '../../admin/types.js'
import type { ImportMap } from '../../bin/generateImportMap/index.js'
Expand Down Expand Up @@ -139,7 +138,7 @@ export const createClientCollectionConfig = ({
clientCollection.admin.description = collection.admin.description
}
} else if (typeof collection.admin.description === 'function') {
const description = collection.admin.description({ t: i18n.t })
const description = collection.admin.description({ t: i18n.t as TFunction })
if (description) {
clientCollection.admin.description = description
}
Expand All @@ -159,7 +158,8 @@ export const createClientCollectionConfig = ({
}
break
default:
clientCollection.admin[adminKey] = collection.admin[adminKey]
;(clientCollection as any).admin[adminKey] =
collection.admin[adminKey as keyof SanitizedCollectionConfig['admin']]
}
}
break
Expand Down Expand Up @@ -215,11 +215,11 @@ export const createClientCollectionConfig = ({
clientCollection.labels = {
plural:
typeof collection.labels.plural === 'function'
? collection.labels.plural({ i18n, t: i18n.t })
? collection.labels.plural({ i18n, t: i18n.t as TFunction })
: collection.labels.plural,
singular:
typeof collection.labels.singular === 'function'
? collection.labels.singular({ i18n, t: i18n.t })
? collection.labels.singular({ i18n, t: i18n.t as TFunction })
: collection.labels.singular,
}
break
Expand All @@ -241,13 +241,14 @@ export const createClientCollectionConfig = ({
return sanitizedSize
})
} else {
clientCollection.upload[uploadKey] = collection.upload[uploadKey]
;(clientCollection.upload as any)[uploadKey] =
collection.upload[uploadKey as keyof SanitizedUploadConfig]
}
}
break

default:
clientCollection[key] = collection[key]
;(clientCollection as any)[key] = collection[key as keyof SanitizedCollectionConfig]
}
}

Expand Down
5 changes: 2 additions & 3 deletions packages/payload/src/collections/dataloader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { BatchLoadFn } from 'dataloader'

import DataLoader from 'dataloader'
Expand All @@ -22,7 +21,7 @@ import { isValidID } from '../utilities/isValidID.js'

const batchAndLoadDocs =
(req: PayloadRequest): BatchLoadFn<string, TypeWithID> =>
async (keys: string[]): Promise<TypeWithID[]> => {
async (keys: readonly string[]): Promise<TypeWithID[]> => {
const { payload } = req

// Create docs array of same length as keys, using null as value
Expand All @@ -47,7 +46,7 @@ const batchAndLoadDocs =
*
**/

const batchByFindArgs = {}
const batchByFindArgs: Record<string, string[]> = {}

for (const key of keys) {
const [
Expand Down
3 changes: 1 addition & 2 deletions packages/payload/src/collections/operations/delete.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { status as httpStatus } from 'http-status'

import type { AccessResult } from '../../config/types.js'
Expand Down Expand Up @@ -271,7 +270,7 @@ export const deleteOperation = async <
} catch (error) {
errors.push({
id: doc.id,
message: error.message,
message: error instanceof Error ? error.message : 'Unknown error',
})
}
return null
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/collections/operations/findVersions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @ts-strict-ignore
import type { AccessResult } from '../../config/types.js'
import type { PaginatedDocs } from '../../database/types.js'
import type { PayloadRequest, PopulateType, SelectType, Sort, Where } from '../../types/index.js'
import type { TypeWithVersion } from '../../versions/types.js'
Expand Down Expand Up @@ -54,7 +54,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
// Access
// /////////////////////////////////////

let accessResults
let accessResults!: AccessResult

if (!overrideAccess) {
accessResults = await executeAccess({ req }, collectionConfig.access.readVersions)
Expand Down
5 changes: 3 additions & 2 deletions packages/payload/src/collections/operations/update.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { DeepPartial } from 'ts-essentials'

import { status as httpStatus } from 'http-status'
Expand Down Expand Up @@ -236,7 +235,7 @@ export const updateOperation = async <
} catch (error) {
errors.push({
id,
message: error.message,
message: error instanceof Error ? error.message : 'Unknown error',
})
}
return null
Expand All @@ -263,13 +262,15 @@ export const updateOperation = async <
args,
collection: collectionConfig,
operation: 'update',
// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
result,
})

if (shouldCommit) {
await commitTransaction(req)
}

// @ts-expect-error - vestiges of when tsconfig was not strict. Feel free to improve
return result
} catch (error: unknown) {
await killTransaction(args.req)
Expand Down
3 changes: 1 addition & 2 deletions packages/payload/src/config/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { I18nClient } from '@payloadcms/translations'
import type { DeepPartial } from 'ts-essentials'

Expand Down Expand Up @@ -203,7 +202,7 @@ export const createClientConfig = ({
}
break
default:
clientConfig[key] = config[key]
;(clientConfig as any)[key] = config[key as keyof SanitizedConfig]
}
}
return clientConfig as ClientConfig
Expand Down
3 changes: 1 addition & 2 deletions packages/payload/src/config/sanitize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { AcceptedLanguages } from '@payloadcms/translations'

import { en } from '@payloadcms/translations/languages/en'
Expand Down Expand Up @@ -315,7 +314,7 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
if (hooks && config?.jobs?.runHooks !== true) {
for (const hook of Object.keys(hooks)) {
const defaultAmount = hook === 'afterRead' || hook === 'beforeChange' ? 1 : 0
if (hooks[hook]?.length > defaultAmount) {
if (hooks[hook as keyof typeof hooks]!.length > defaultAmount) {
// eslint-disable-next-line no-console
console.warn(
`The jobsCollectionOverrides function is returning a collection with an additional ${hook} hook defined. These hooks will not run unless the jobs.runHooks option is set to true. Setting this option to true will negatively impact performance.`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { SanitizedCollectionConfig } from '../../collections/config/types.js'
import type { FlattenedField } from '../../fields/config/types.js'
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
Expand Down Expand Up @@ -82,7 +81,7 @@ export async function validateQueryPaths({
}
} else if (!Array.isArray(constraint)) {
for (const operator in constraint) {
const val = constraint[operator]
const val = constraint[operator as keyof typeof constraint]
if (validOperatorSet.has(operator as Operator)) {
promises.push(
validateSearchParam({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { SanitizedCollectionConfig } from '../../collections/config/types.js'
import type { FlattenedField } from '../../fields/config/types.js'
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
Expand Down Expand Up @@ -109,8 +108,8 @@ export async function validateSearchParam({
if (field.virtual === true) {
errors.push({ path })
} else {
constraint[`${field.virtual}`] = constraint[path]
delete constraint[path]
constraint[`${field.virtual}` as keyof WhereField] = constraint[path as keyof WhereField]
delete constraint[path as keyof WhereField]
}
}

Expand Down Expand Up @@ -155,7 +154,7 @@ export async function validateSearchParam({
const entitySlug = collectionSlug || globalConfig!.slug
const segments = fieldPath.split('.')

let fieldAccess
let fieldAccess: any

if (versionFields) {
fieldAccess = policies[entityType]![entitySlug]!
Expand Down
13 changes: 7 additions & 6 deletions packages/payload/src/database/sanitizeJoinQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { SanitizedCollectionConfig, SanitizedJoin } from '../collections/config/types.js'
import type { JoinQuery, PayloadRequest } from '../types/index.js'

Expand Down Expand Up @@ -33,7 +32,9 @@ const sanitizeJoinFieldQuery = async ({
}) => {
const { joinPath } = join

if (joinsQuery[joinPath] === false) {
// TODO: fix any's in joinsQuery[joinPath]

if ((joinsQuery as any)[joinPath] === false) {
return
}

Expand All @@ -44,15 +45,15 @@ const sanitizeJoinFieldQuery = async ({
: true

if (accessResult === false) {
joinsQuery[joinPath] = false
;(joinsQuery as any)[joinPath] = false
return
}

if (!joinsQuery[joinPath]) {
joinsQuery[joinPath] = {}
if (!(joinsQuery as any)[joinPath]) {
;(joinsQuery as any)[joinPath] = {}
}

const joinQuery = joinsQuery[joinPath]
const joinQuery = (joinsQuery as any)[joinPath]

if (!joinQuery.where) {
joinQuery.where = {}
Expand Down
Loading
Loading