Add null value support to BaseEmailDocument interface

- Update BaseEmailDocument to support `| null` for optional fields (cc, bcc, from, replyTo, text, etc.)
- Update BaseEmailTemplateDocument to support `| null` for optional fields
- Add explicit null checks in sendEmail processing to handle null values properly
- Update CUSTOM-TYPES.md documentation to reflect null value compatibility

Fixes type constraint error where customer Email types had `cc?: string[] | null`
but BaseEmailDocument only supported `cc?: string[]`.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-13 23:27:53 +02:00
parent 4c495a72b0
commit 8809db6aff
3 changed files with 44 additions and 42 deletions

View File

@@ -38,13 +38,15 @@ const customEmail = await sendEmail<MyEmail>(payload, {
}) })
``` ```
## ID Type Compatibility ## Compatibility
The plugin works with both: The plugin works with:
- **String IDs**: `id: string` - **String IDs**: `id: string`
- **Number IDs**: `id: number` - **Number IDs**: `id: number`
- **Nullable fields**: Fields can be `null`, `undefined`, or have values
- **Generated types**: Works with `payload generate:types` output
Your Payload configuration determines which type is used. The plugin automatically adapts to your setup. Your Payload configuration determines which types are used. The plugin automatically adapts to your setup.
## Type Definitions ## Type Definitions
@@ -55,33 +57,33 @@ interface BaseEmailDocument {
id: string | number id: string | number
template?: any template?: any
to: string[] to: string[]
cc?: string[] cc?: string[] | null
bcc?: string[] bcc?: string[] | null
from?: string from?: string | null
replyTo?: string replyTo?: string | null
subject: string subject: string
html: string html: string
text?: string text?: string | null
variables?: Record<string, any> variables?: Record<string, any> | null
scheduledAt?: string scheduledAt?: string | null
sentAt?: string sentAt?: string | null
status?: 'pending' | 'processing' | 'sent' | 'failed' status?: 'pending' | 'processing' | 'sent' | 'failed' | null
attempts?: number attempts?: number | null
lastAttemptAt?: string lastAttemptAt?: string | null
error?: string error?: string | null
priority?: number priority?: number | null
createdAt?: string createdAt?: string | null
updatedAt?: string updatedAt?: string | null
} }
interface BaseEmailTemplateDocument { interface BaseEmailTemplateDocument {
id: string | number id: string | number
name: string name: string
slug: string slug: string
subject?: string subject?: string | null
content?: any content?: any
createdAt?: string createdAt?: string | null
updatedAt?: string updatedAt?: string | null
} }
``` ```

View File

@@ -83,10 +83,10 @@ export const sendEmail = async <TEmail extends BaseEmailDocument = BaseEmailDocu
if (emailData.to) { if (emailData.to) {
emailData.to = parseAndValidateEmails(emailData.to as string | string[]) emailData.to = parseAndValidateEmails(emailData.to as string | string[])
} }
if (emailData.cc) { if (emailData.cc && emailData.cc !== null) {
emailData.cc = parseAndValidateEmails(emailData.cc as string | string[]) emailData.cc = parseAndValidateEmails(emailData.cc as string | string[])
} }
if (emailData.bcc) { if (emailData.bcc && emailData.bcc !== null) {
emailData.bcc = parseAndValidateEmails(emailData.bcc as string | string[]) emailData.bcc = parseAndValidateEmails(emailData.bcc as string | string[])
} }

View File

@@ -2,38 +2,38 @@ import { Payload } from 'payload'
import type { CollectionConfig, RichTextField } from 'payload' import type { CollectionConfig, RichTextField } from 'payload'
import { Transporter } from 'nodemailer' import { Transporter } from 'nodemailer'
// Generic base interfaces that work with any ID type // Generic base interfaces that work with any ID type and null values
export interface BaseEmailDocument { export interface BaseEmailDocument {
id: string | number id: string | number
template?: any template?: any
to: string[] to: string[]
cc?: string[] cc?: string[] | null
bcc?: string[] bcc?: string[] | null
from?: string from?: string | null
replyTo?: string replyTo?: string | null
subject: string subject: string
html: string html: string
text?: string text?: string | null
variables?: Record<string, any> variables?: Record<string, any> | null
scheduledAt?: string scheduledAt?: string | null
sentAt?: string sentAt?: string | null
status?: 'pending' | 'processing' | 'sent' | 'failed' status?: 'pending' | 'processing' | 'sent' | 'failed' | null
attempts?: number attempts?: number | null
lastAttemptAt?: string lastAttemptAt?: string | null
error?: string error?: string | null
priority?: number priority?: number | null
createdAt?: string createdAt?: string | null
updatedAt?: string updatedAt?: string | null
} }
export interface BaseEmailTemplateDocument { export interface BaseEmailTemplateDocument {
id: string | number id: string | number
name: string name: string
slug: string slug: string
subject?: string subject?: string | null
content?: any content?: any
createdAt?: string createdAt?: string | null
updatedAt?: string updatedAt?: string | null
} }
export type BaseEmail<TEmail extends BaseEmailDocument = BaseEmailDocument, TEmailTemplate extends BaseEmailTemplateDocument = BaseEmailTemplateDocument> = Omit<TEmail, 'id' | 'template'> & {template: Omit<TEmailTemplate, 'id'> | TEmailTemplate['id'] | undefined | null} export type BaseEmail<TEmail extends BaseEmailDocument = BaseEmailDocument, TEmailTemplate extends BaseEmailTemplateDocument = BaseEmailTemplateDocument> = Omit<TEmail, 'id' | 'template'> & {template: Omit<TEmailTemplate, 'id'> | TEmailTemplate['id'] | undefined | null}