- Remove .claude/skills (symlinks) e a árvore .cursor (rules, hooks, skills) - Adiciona .agents/ com skills (+ references/), rules/, hooks/ e scripts - Adiciona .codex/hooks.json (hook do impeccable) - Corrige referências .cursor → .agents em environment.json, hooks.json e AGENTS.md
6.6 KiB
6.6 KiB
| title | description | tags | ||||
|---|---|---|---|---|---|---|
| Field Type Guards | Runtime field type checking and safe type narrowing |
|
Payload Field Type Guards
Type guards for runtime field type checking and safe type narrowing.
Most Common Guards
fieldAffectsData
Most commonly used guard. Checks if field stores data (has name and is not UI-only).
import { fieldAffectsData } from 'payload'
function generateSchema(fields: Field[]) {
fields.forEach((field) => {
if (fieldAffectsData(field)) {
// Safe to access field.name
schema[field.name] = getFieldType(field)
}
})
}
// Filter data fields
const dataFields = fields.filter(fieldAffectsData)
fieldHasSubFields
Checks if field contains nested fields (group, array, row, or collapsible).
import { fieldHasSubFields } from 'payload'
function traverseFields(fields: Field[]): void {
fields.forEach((field) => {
if (fieldHasSubFields(field)) {
// Safe to access field.fields
traverseFields(field.fields)
}
})
}
fieldIsArrayType
Checks if field type is 'array'.
import { fieldIsArrayType } from 'payload'
if (fieldIsArrayType(field)) {
// field.type === 'array'
console.log(`Min rows: ${field.minRows}`)
console.log(`Max rows: ${field.maxRows}`)
}
Capability Guards
fieldSupportsMany
Checks if field can have multiple values (select, relationship, or upload with hasMany).
import { fieldSupportsMany } from 'payload'
if (fieldSupportsMany(field)) {
// field.type is 'select' | 'relationship' | 'upload'
if (field.hasMany) {
console.log('Field accepts multiple values')
}
}
fieldHasMaxDepth
Checks if field is relationship/upload/join with numeric maxDepth property.
import { fieldHasMaxDepth } from 'payload'
if (fieldHasMaxDepth(field)) {
// field.type is 'upload' | 'relationship' | 'join'
// AND field.maxDepth is number
const remainingDepth = field.maxDepth - currentDepth
}
fieldIsVirtual
Checks if field is virtual (computed or virtual relationship).
import { fieldIsVirtual } from 'payload'
if (fieldIsVirtual(field)) {
// field.virtual is truthy
if (typeof field.virtual === 'string') {
console.log(`Virtual path: ${field.virtual}`)
}
}
Type Checking Guards
fieldIsBlockType
import { fieldIsBlockType } from 'payload'
if (fieldIsBlockType(field)) {
// field.type === 'blocks'
field.blocks.forEach((block) => {
console.log(`Block: ${block.slug}`)
})
}
fieldIsGroupType
import { fieldIsGroupType } from 'payload'
if (fieldIsGroupType(field)) {
// field.type === 'group'
console.log(`Interface: ${field.interfaceName}`)
}
fieldIsPresentationalOnly
import { fieldIsPresentationalOnly } from 'payload'
if (fieldIsPresentationalOnly(field)) {
// field.type === 'ui'
// Skip in data operations, GraphQL schema, etc.
return
}
Common Patterns
Recursive Field Traversal
import { fieldAffectsData, fieldHasSubFields } from 'payload'
function traverseFields(fields: Field[], callback: (field: Field) => void) {
fields.forEach((field) => {
if (fieldAffectsData(field)) {
callback(field)
}
if (fieldHasSubFields(field)) {
traverseFields(field.fields, callback)
}
})
}
Filter Data-Bearing Fields
import { fieldAffectsData, fieldIsPresentationalOnly, fieldIsHiddenOrDisabled } from 'payload'
const dataFields = fields.filter(
(field) =>
fieldAffectsData(field) && !fieldIsPresentationalOnly(field) && !fieldIsHiddenOrDisabled(field),
)
Container Type Switching
import { fieldIsArrayType, fieldIsBlockType, fieldHasSubFields } from 'payload'
if (fieldIsArrayType(field)) {
// Handle array-specific logic
} else if (fieldIsBlockType(field)) {
// Handle blocks-specific logic
} else if (fieldHasSubFields(field)) {
// Handle group/row/collapsible
}
Safe Property Access
import { fieldSupportsMany, fieldHasMaxDepth } from 'payload'
// With guard - safe access
if (fieldSupportsMany(field) && field.hasMany) {
console.log('Multiple values supported')
}
if (fieldHasMaxDepth(field)) {
const depth = field.maxDepth // TypeScript knows this is number
}
All Available Guards
| Type Guard | Checks For | Use When |
|---|---|---|
fieldAffectsData |
Field stores data (has name) | Need to access field data or name |
fieldHasSubFields |
Field contains nested fields | Recursively traverse fields |
fieldIsArrayType |
Field is array type | Distinguish arrays from other containers |
fieldIsBlockType |
Field is blocks type | Handle blocks-specific logic |
fieldIsGroupType |
Field is group type | Handle group-specific logic |
fieldSupportsMany |
Field can have multiple values | Check for hasMany support |
fieldHasMaxDepth |
Field supports depth control | Control relationship/upload/join depth |
fieldIsPresentationalOnly |
Field is UI-only | Exclude from data operations |
fieldIsSidebar |
Field positioned in sidebar | Separate sidebar rendering |
fieldIsID |
Field name is 'id' | Special ID field handling |
fieldIsHiddenOrDisabled |
Field is hidden or disabled | Filter from UI operations |
fieldShouldBeLocalized |
Field needs localization | Proper locale table checks |
fieldIsVirtual |
Field is virtual | Skip in database transforms |
tabHasName |
Tab is named (stores data) | Distinguish named vs unnamed tabs |
groupHasName |
Group is named (stores data) | Distinguish named vs unnamed groups |
optionIsObject |
Option is {label, value} |
Access option properties safely |
optionsAreObjects |
All options are objects | Batch option processing |
optionIsValue |
Option is string value | Handle string options |
valueIsValueWithRelation |
Value is polymorphic relationship | Handle polymorphic relationships |