From bb7c829c5ec2e28b6f6bc78ce8f3bd576f206e41 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 30 Nov 2020 20:12:15 -0500 Subject: [PATCH] progress to typing Blocks and Arrays --- components/forms.js | 2 +- .../components/elements/StepNav/index.tsx | 6 +- .../forms/DraggableSection/index.tsx | 44 +------ .../forms/DraggableSection/types.ts | 23 ++++ src/admin/components/forms/Label/index.tsx | 21 +--- src/admin/components/forms/Label/types.ts | 5 + .../components/forms/RenderFields/types.ts | 10 +- .../forms/field-types/Array/Array.tsx | 110 ++--------------- .../forms/field-types/Array/index.tsx | 2 +- .../forms/field-types/Array/types.ts | 32 +++++ .../Blocks/BlockSelector/index.tsx | 3 +- .../field-types/Blocks/BlockSelector/types.ts | 3 + .../forms/field-types/Blocks/Blocks.tsx | 116 ++---------------- .../forms/field-types/Blocks/index.tsx | 2 +- .../forms/field-types/Blocks/types.ts | 33 +++++ .../views/collections/Edit/Default.tsx | 1 - .../views/collections/Edit/index.tsx | 2 +- src/auth/types.ts | 41 +++---- src/fields/config/types.ts | 6 +- 19 files changed, 155 insertions(+), 307 deletions(-) create mode 100644 src/admin/components/forms/DraggableSection/types.ts create mode 100644 src/admin/components/forms/Label/types.ts create mode 100644 src/admin/components/forms/field-types/Array/types.ts create mode 100644 src/admin/components/forms/field-types/Blocks/BlockSelector/types.ts create mode 100644 src/admin/components/forms/field-types/Blocks/types.ts diff --git a/components/forms.js b/components/forms.js index 3038d75316..1c293d73bb 100644 --- a/components/forms.js +++ b/components/forms.js @@ -1,6 +1,6 @@ export { useForm, - useFormFields, + useWatchForm, useFormSubmitted, useFormProcessing, useFormModified, diff --git a/src/admin/components/elements/StepNav/index.tsx b/src/admin/components/elements/StepNav/index.tsx index 6d468a8e0b..c16ac8afa1 100644 --- a/src/admin/components/elements/StepNav/index.tsx +++ b/src/admin/components/elements/StepNav/index.tsx @@ -3,11 +3,11 @@ import React, { } from 'react'; import { Link } from 'react-router-dom'; import Chevron from '../../icons/Chevron'; -import { Context } from './types'; +import { Context as ContextType } from './types'; import './index.scss'; -const Context = createContext({} as Context); +const Context = createContext({} as ContextType); const StepNavProvider: React.FC = ({ children }) => { const [stepNav, setStepNav] = useState([]); @@ -23,7 +23,7 @@ const StepNavProvider: React.FC = ({ children }) => { ); }; -const useStepNav = (): Context => useContext(Context); +const useStepNav = (): ContextType => useContext(Context); const StepNav: React.FC = () => { const dashboardLabel = Dashboard; diff --git a/src/admin/components/forms/DraggableSection/index.tsx b/src/admin/components/forms/DraggableSection/index.tsx index bbecb76eb0..d6c1f91e77 100644 --- a/src/admin/components/forms/DraggableSection/index.tsx +++ b/src/admin/components/forms/DraggableSection/index.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import PropTypes from 'prop-types'; import AnimateHeight from 'react-animate-height'; import { Draggable } from 'react-beautiful-dnd'; @@ -10,12 +9,13 @@ import Button from '../../elements/Button'; import { NegativeFieldGutterProvider } from '../FieldTypeGutter/context'; import FieldTypeGutter from '../FieldTypeGutter'; import RenderFields from '../RenderFields'; +import { Props } from './types'; import './index.scss'; const baseClass = 'draggable-section'; -const DraggableSection = (props) => { +const DraggableSection: React.FC = (props) => { const { moveRow, addRow, @@ -29,8 +29,8 @@ const DraggableSection = (props) => { fieldTypes, toggleRowCollapse, id, - positionPanelVerticalAlignment, - actionPanelVerticalAlignment, + positionPanelVerticalAlignment = 'sticky', + actionPanelVerticalAlignment = 'sticky', permissions, isOpen, readOnly, @@ -102,7 +102,7 @@ const DraggableSection = (props) => { readOnly={readOnly} fieldTypes={fieldTypes} key={rowIndex} - permissions={permissions} + permissions={permissions?.fields} fieldSchema={fieldSchema.map((field) => ({ ...field, path: `${parentPath}.${rowIndex}${field.name ? `.${field.name}` : ''}`, @@ -136,38 +136,4 @@ const DraggableSection = (props) => { ); }; -DraggableSection.defaultProps = { - toggleRowCollapse: undefined, - rowCount: null, - initialData: undefined, - label: '', - blockType: '', - isOpen: true, - positionPanelVerticalAlignment: 'sticky', - actionPanelVerticalAlignment: 'sticky', - permissions: {}, - readOnly: false, -}; - -DraggableSection.propTypes = { - moveRow: PropTypes.func.isRequired, - addRow: PropTypes.func.isRequired, - removeRow: PropTypes.func.isRequired, - toggleRowCollapse: PropTypes.func, - rowIndex: PropTypes.number.isRequired, - parentPath: PropTypes.string.isRequired, - label: PropTypes.string, - fieldSchema: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - rowCount: PropTypes.number, - initialData: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.shape({})]), - isOpen: PropTypes.bool, - blockType: PropTypes.string, - fieldTypes: PropTypes.shape({}).isRequired, - id: PropTypes.string.isRequired, - positionPanelVerticalAlignment: PropTypes.oneOf(['top', 'center', 'sticky']), - actionPanelVerticalAlignment: PropTypes.oneOf(['top', 'center', 'sticky']), - permissions: PropTypes.shape({}), - readOnly: PropTypes.bool, -}; - export default DraggableSection; diff --git a/src/admin/components/forms/DraggableSection/types.ts b/src/admin/components/forms/DraggableSection/types.ts new file mode 100644 index 0000000000..d417454dc4 --- /dev/null +++ b/src/admin/components/forms/DraggableSection/types.ts @@ -0,0 +1,23 @@ +import { Field } from '../../../../fields/config/types'; +import { FieldTypes } from '../field-types'; +import { FieldPermissions } from '../../../../auth/types'; + +export type Props = { + moveRow: (fromIndex: number, toIndex: number) => void + addRow: (index: number, blockType?: string) => void + removeRow: (index: number) => void + rowIndex: number + rowCount: number + parentPath: string + fieldSchema: Field[], + label?: string + blockType?: string + fieldTypes: FieldTypes + toggleRowCollapse?: (index: number) => void + id: string + positionPanelVerticalAlignment?: 'top' | 'center' | 'sticky' + actionPanelVerticalAlignment?: 'top' | 'center' | 'sticky' + permissions: FieldPermissions + isOpen?: boolean + readOnly: boolean +} diff --git a/src/admin/components/forms/Label/index.tsx b/src/admin/components/forms/Label/index.tsx index 6483bc6013..2d77f41c4a 100644 --- a/src/admin/components/forms/Label/index.tsx +++ b/src/admin/components/forms/Label/index.tsx @@ -1,11 +1,10 @@ import React from 'react'; -import PropTypes from 'prop-types'; - +import { Props } from './types'; import './index.scss'; -const Label = (props) => { +const Label: React.FC = (props) => { const { - label, required, htmlFor, + label, required = false, htmlFor, } = props; if (label) { @@ -23,18 +22,4 @@ const Label = (props) => { return null; }; -Label.defaultProps = { - required: false, - label: '', -}; - -Label.propTypes = { - label: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.node, - ]), - htmlFor: PropTypes.string.isRequired, - required: PropTypes.bool, -}; - export default Label; diff --git a/src/admin/components/forms/Label/types.ts b/src/admin/components/forms/Label/types.ts new file mode 100644 index 0000000000..16ded9d019 --- /dev/null +++ b/src/admin/components/forms/Label/types.ts @@ -0,0 +1,5 @@ +export type Props = { + label?: string + required?: boolean + htmlFor?: string +} diff --git a/src/admin/components/forms/RenderFields/types.ts b/src/admin/components/forms/RenderFields/types.ts index c6b67ddd2f..c20b2e3452 100644 --- a/src/admin/components/forms/RenderFields/types.ts +++ b/src/admin/components/forms/RenderFields/types.ts @@ -1,5 +1,5 @@ -import { CollectionFieldPermissions, GlobalFieldPermissions } from '../../../../auth/types'; -import { FieldWithPath } from '../../../../fields/config/types'; +import { FieldPermissions } from '../../../../auth/types'; +import { FieldWithPath, Field } from '../../../../fields/config/types'; import { FieldTypes } from '../field-types'; export type Operation = 'create' | 'update' @@ -12,8 +12,10 @@ export type Props = { className?: string operation: Operation readOnly: boolean - permissions: CollectionFieldPermissions | GlobalFieldPermissions - filter: (field: Field) => boolean + permissions: { + [field: string]: FieldPermissions + } + filter?: (field: Field) => boolean fieldSchema: FieldWithPath[] fieldTypes: FieldTypes } diff --git a/src/admin/components/forms/field-types/Array/Array.tsx b/src/admin/components/forms/field-types/Array/Array.tsx index 497da924c2..5d7af64c0a 100644 --- a/src/admin/components/forms/field-types/Array/Array.tsx +++ b/src/admin/components/forms/field-types/Array/Array.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useReducer, useCallback, useState } from 'react'; -import PropTypes from 'prop-types'; import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import withCondition from '../../withCondition'; @@ -13,23 +12,27 @@ import Error from '../../Error'; import { array } from '../../../../../fields/validations'; import getDataByPath from '../../Form/getDataByPath'; import Banner from '../../../elements/Banner'; +import { Props, RenderArrayProps } from './types'; import './index.scss'; const baseClass = 'field-type array'; -const ArrayFieldType = (props) => { +const ArrayFieldType: React.FC = (props) => { const { label, name, path: pathFromProps, fields, fieldTypes, - validate, + validate = array, required, maxRows, minRows, - labels, + labels = { + singular: 'Row', + plural: 'Rows', + }, permissions, admin: { readOnly, @@ -125,45 +128,7 @@ const ArrayFieldType = (props) => { ); }; -ArrayFieldType.defaultProps = { - label: undefined, - validate: array, - required: false, - maxRows: undefined, - minRows: undefined, - labels: { - singular: 'Row', - plural: 'Rows', - }, - permissions: {}, - admin: {}, -}; - -ArrayFieldType.propTypes = { - fields: PropTypes.arrayOf( - PropTypes.shape({}), - ).isRequired, - label: PropTypes.string, - labels: PropTypes.shape({ - singular: PropTypes.string, - plural: PropTypes.string, - }), - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - fieldTypes: PropTypes.shape({}).isRequired, - validate: PropTypes.func, - required: PropTypes.bool, - maxRows: PropTypes.number, - minRows: PropTypes.number, - permissions: PropTypes.shape({ - fields: PropTypes.shape({}), - }), - admin: PropTypes.shape({ - readOnly: PropTypes.bool, - }), -}; - -const RenderArray = React.memo((props) => { +const RenderArray = React.memo((props: RenderArrayProps) => { const { onDragEnd, label, @@ -180,8 +145,6 @@ const RenderArray = React.memo((props) => { permissions, value, readOnly, - style, - width, minRows, maxRows, required, @@ -191,10 +154,6 @@ const RenderArray = React.memo((props) => {

{label}

@@ -223,7 +182,6 @@ const RenderArray = React.memo((props) => { removeRow={() => removeRow(i)} moveRow={moveRow} parentPath={path} - initNull={row.initNull} fieldTypes={fieldTypes} fieldSchema={fields} permissions={permissions.fields} @@ -268,56 +226,4 @@ const RenderArray = React.memo((props) => { ); }); -RenderArray.defaultProps = { - label: undefined, - showError: false, - errorMessage: undefined, - rows: [], - labels: { - singular: 'Row', - plural: 'Rows', - }, - path: '', - value: undefined, - readOnly: false, - style: {}, - width: undefined, - maxRows: undefined, - minRows: undefined, - required: false, -}; - -RenderArray.propTypes = { - label: PropTypes.string, - showError: PropTypes.bool, - errorMessage: PropTypes.string, - rows: PropTypes.arrayOf( - PropTypes.shape({}), - ), - labels: PropTypes.shape({ - singular: PropTypes.string, - plural: PropTypes.string, - }), - path: PropTypes.string, - name: PropTypes.string.isRequired, - value: PropTypes.number, - onDragEnd: PropTypes.func.isRequired, - addRow: PropTypes.func.isRequired, - removeRow: PropTypes.func.isRequired, - moveRow: PropTypes.func.isRequired, - fieldTypes: PropTypes.shape({}).isRequired, - fields: PropTypes.arrayOf( - PropTypes.shape({}), - ).isRequired, - permissions: PropTypes.shape({ - fields: PropTypes.shape({}), - }).isRequired, - readOnly: PropTypes.bool, - style: PropTypes.shape({}), - width: PropTypes.string, - maxRows: PropTypes.number, - minRows: PropTypes.number, - required: PropTypes.bool, -}; - export default withCondition(ArrayFieldType); diff --git a/src/admin/components/forms/field-types/Array/index.tsx b/src/admin/components/forms/field-types/Array/index.tsx index 7ead06b5da..8364140b8a 100644 --- a/src/admin/components/forms/field-types/Array/index.tsx +++ b/src/admin/components/forms/field-types/Array/index.tsx @@ -3,7 +3,7 @@ import Loading from '../../../elements/Loading'; const ArrayField = lazy(() => import('./Array')); -export default (props) => ( +export default (props: unknown): React.ReactNode => ( }> diff --git a/src/admin/components/forms/field-types/Array/types.ts b/src/admin/components/forms/field-types/Array/types.ts new file mode 100644 index 0000000000..7ad186e886 --- /dev/null +++ b/src/admin/components/forms/field-types/Array/types.ts @@ -0,0 +1,32 @@ +import { Data } from '../../Form/types'; +import { ArrayField, Labels, Field } from '../../../../../fields/config/types'; +import { FieldTypes } from '..'; +import { FieldPermissions } from '../../../../../auth/types'; + +export type Props = ArrayField & { + path?: string + fieldTypes: FieldTypes + permissions: FieldPermissions +} + +export type RenderArrayProps = { + path: string + name: string + fieldTypes: FieldTypes + fields: Field[] + permissions: FieldPermissions + onDragEnd: (result: any) => void + label: string + value: number + readOnly: boolean + minRows: number + maxRows: number + required: boolean + labels: Labels + addRow: (index: number) => Promise + removeRow: (index: number) => void + moveRow: (fromIndex: number, toIndex: number) => void + showError: boolean + errorMessage: string + rows: Data[] +} diff --git a/src/admin/components/forms/field-types/Blocks/BlockSelector/index.tsx b/src/admin/components/forms/field-types/Blocks/BlockSelector/index.tsx index ae5dca6a27..1c52a3bafb 100644 --- a/src/admin/components/forms/field-types/Blocks/BlockSelector/index.tsx +++ b/src/admin/components/forms/field-types/Blocks/BlockSelector/index.tsx @@ -3,10 +3,11 @@ import PropTypes from 'prop-types'; import BlockSearch from './BlockSearch'; import BlocksContainer from './BlocksContainer'; +import { Props } from './types'; const baseClass = 'block-selector'; -const BlockSelector = (props) => { +const BlockSelector: React.FC = (props) => { const { blocks, close, parentIsHovered, watchParentHover, ...remainingProps } = props; diff --git a/src/admin/components/forms/field-types/Blocks/BlockSelector/types.ts b/src/admin/components/forms/field-types/Blocks/BlockSelector/types.ts new file mode 100644 index 0000000000..daededa188 --- /dev/null +++ b/src/admin/components/forms/field-types/Blocks/BlockSelector/types.ts @@ -0,0 +1,3 @@ +export type Props = { + +} diff --git a/src/admin/components/forms/field-types/Blocks/Blocks.tsx b/src/admin/components/forms/field-types/Blocks/Blocks.tsx index 4aaddc719f..c4278f9373 100644 --- a/src/admin/components/forms/field-types/Blocks/Blocks.tsx +++ b/src/admin/components/forms/field-types/Blocks/Blocks.tsx @@ -17,28 +17,30 @@ import BlockSelector from './BlockSelector'; import { blocks as blocksValidator } from '../../../../../fields/validations'; import getDataByPath from '../../Form/getDataByPath'; import Banner from '../../../elements/Banner'; +import { Props, RenderBlockProps } from './types'; import './index.scss'; const baseClass = 'field-type blocks'; -const Blocks = (props) => { +const Blocks: React.FC = (props) => { const { label, name, path: pathFromProps, blocks, - labels, + labels = { + singular: 'Block', + plural: 'Blocks', + }, fieldTypes, maxRows, minRows, required, - validate, + validate = blocksValidator, permissions, admin: { readOnly, - style, - width, }, } = props; @@ -138,55 +140,14 @@ const Blocks = (props) => { value={value} blocks={blocks} readOnly={readOnly} - style={style} - width={width} minRows={minRows} + maxRows={maxRows} required={required} /> ); }; -Blocks.defaultProps = { - label: '', - labels: { - singular: 'Block', - plural: 'Blocks', - }, - validate: blocksValidator, - required: false, - maxRows: undefined, - minRows: undefined, - permissions: {}, - admin: {}, -}; - -Blocks.propTypes = { - blocks: PropTypes.arrayOf( - PropTypes.shape({}), - ).isRequired, - label: PropTypes.string, - labels: PropTypes.shape({ - singular: PropTypes.string, - plural: PropTypes.string, - }), - name: PropTypes.string.isRequired, - path: PropTypes.string.isRequired, - fieldTypes: PropTypes.shape({}).isRequired, - validate: PropTypes.func, - required: PropTypes.bool, - maxRows: PropTypes.number, - minRows: PropTypes.number, - permissions: PropTypes.shape({ - fields: PropTypes.shape({}), - }), - admin: PropTypes.shape({ - readOnly: PropTypes.bool, - style: PropTypes.shape({}), - width: PropTypes.string, - }), -}; - -const RenderBlocks = React.memo((props) => { +const RenderBlocks = React.memo((props: RenderBlockProps) => { const { onDragEnd, label, @@ -204,8 +165,6 @@ const RenderBlocks = React.memo((props) => { toggleCollapse, blocks, readOnly, - style, - width, minRows, maxRows, required, @@ -215,10 +174,6 @@ const RenderBlocks = React.memo((props) => {

{label}

@@ -331,57 +286,4 @@ const RenderBlocks = React.memo((props) => { ); }); -RenderBlocks.defaultProps = { - label: undefined, - showError: false, - errorMessage: undefined, - rows: [], - labels: { - singular: 'Block', - plural: 'Blocks', - }, - path: '', - value: undefined, - readOnly: false, - style: {}, - width: undefined, - maxRows: undefined, - minRows: undefined, - required: false, -}; - -RenderBlocks.propTypes = { - label: PropTypes.string, - showError: PropTypes.bool, - errorMessage: PropTypes.string, - rows: PropTypes.arrayOf( - PropTypes.shape({}), - ), - labels: PropTypes.shape({ - singular: PropTypes.string, - plural: PropTypes.string, - }), - path: PropTypes.string, - name: PropTypes.string.isRequired, - value: PropTypes.number, - onDragEnd: PropTypes.func.isRequired, - addRow: PropTypes.func.isRequired, - removeRow: PropTypes.func.isRequired, - moveRow: PropTypes.func.isRequired, - fieldTypes: PropTypes.shape({}).isRequired, - permissions: PropTypes.shape({ - fields: PropTypes.shape({}), - }).isRequired, - blocks: PropTypes.arrayOf( - PropTypes.shape({}), - ).isRequired, - toggleCollapse: PropTypes.func.isRequired, - readOnly: PropTypes.bool, - style: PropTypes.shape({}), - width: PropTypes.string, - maxRows: PropTypes.number, - minRows: PropTypes.number, - required: PropTypes.bool, -}; - export default withCondition(Blocks); diff --git a/src/admin/components/forms/field-types/Blocks/index.tsx b/src/admin/components/forms/field-types/Blocks/index.tsx index dcf63c375f..3a85ee08d3 100644 --- a/src/admin/components/forms/field-types/Blocks/index.tsx +++ b/src/admin/components/forms/field-types/Blocks/index.tsx @@ -3,7 +3,7 @@ import Loading from '../../../elements/Loading'; const Blocks = lazy(() => import('./Blocks')); -export default (props) => ( +export default (props: unknown): React.ReactNode => ( }> diff --git a/src/admin/components/forms/field-types/Blocks/types.ts b/src/admin/components/forms/field-types/Blocks/types.ts new file mode 100644 index 0000000000..103d854c1b --- /dev/null +++ b/src/admin/components/forms/field-types/Blocks/types.ts @@ -0,0 +1,33 @@ +import { Data } from '../../Form/types'; +import { BlockField, Labels, Block } from '../../../../../fields/config/types'; +import { FieldTypes } from '..'; +import { FieldPermissions } from '../../../../../auth/types'; + +export type Props = BlockField & { + path?: string + fieldTypes: FieldTypes + permissions: FieldPermissions +} + +export type RenderBlockProps = { + path: string + name: string + fieldTypes: FieldTypes + permissions: FieldPermissions + onDragEnd: (result: any) => void + label: string + value: number + readOnly: boolean + minRows: number + maxRows: number + required: boolean + labels: Labels + addRow: (index: number, blockType: string) => Promise + removeRow: (index: number) => void + moveRow: (fromIndex: number, toIndex: number) => void + showError: boolean + errorMessage: string + rows: Data[] + blocks: Block[], + toggleCollapse: (row: number) => void +} diff --git a/src/admin/components/views/collections/Edit/Default.tsx b/src/admin/components/views/collections/Edit/Default.tsx index 7f4ba92db1..a6f2e13255 100644 --- a/src/admin/components/views/collections/Edit/Default.tsx +++ b/src/admin/components/views/collections/Edit/Default.tsx @@ -169,7 +169,6 @@ const DefaultEditView = (props) => { readOnly={!hasSavePermission} permissions={permissions.fields} filter={(field) => field?.admin?.position === 'sidebar'} - position="sidebar" fieldTypes={fieldTypes} fieldSchema={fields} /> diff --git a/src/admin/components/views/collections/Edit/index.tsx b/src/admin/components/views/collections/Edit/index.tsx index c6fb5409dd..0ef13ef8c1 100644 --- a/src/admin/components/views/collections/Edit/index.tsx +++ b/src/admin/components/views/collections/Edit/index.tsx @@ -27,7 +27,7 @@ const EditView = (props) => { Edit: CustomEdit, } = {}, } = {}, - }, + } = {}, fields, } = collection; diff --git a/src/auth/types.ts b/src/auth/types.ts index f6f3ad903d..2260613b6a 100644 --- a/src/auth/types.ts +++ b/src/auth/types.ts @@ -6,21 +6,17 @@ export type Permission = { where?: Record } -export type CollectionFieldPermissions = { - [field: string]: { - create: { - permission: boolean - } - read: { - permission: boolean - } - update: { - permission: boolean - } - delete: { - permission: boolean - } +export type FieldPermissions = { + create: { + permission: boolean } + read: { + permission: boolean + } + update: { + permission: boolean + } + fields?: FieldPermissions } export type CollectionPermission = { @@ -28,24 +24,17 @@ export type CollectionPermission = { read: Permission update: Permission delete: Permission - fields: CollectionFieldPermissions -} - -export type GlobalFieldPermissions = { - [field: string]: { - read: { - permission: boolean - } - update: { - permission: boolean - } + fields: { + [field: string]: FieldPermissions } } export type GlobalPermission = { read: Permission update: Permission - fields: GlobalFieldPermissions + fields: { + [field: string]: FieldPermissions + } } export type Permissions = { diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index 2cdecfbbee..3c5c464e29 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -25,11 +25,13 @@ type Admin = { hidden?: boolean } -type Labels = { +export type Labels = { singular: string; plural: string; }; +export type Validate = (value: unknown, options: unknown) => string | boolean; + interface FieldBase { name?: string; label?: string; @@ -41,7 +43,7 @@ interface FieldBase { hidden?: boolean; saveToJWT?: boolean localized?: boolean; - validate?: (value: any, field: Field) => any; + validate?: Validate; hooks?: { beforeValidate?: FieldHook[]; beforeChange?: FieldHook[];