validate Property on Fields for example TextField overrides the validate which breaks TS
Link to reproduction
No response
Payload Version
v3.0.0-beta.74
Node Version
v20.16.0
Next.js Version
14.2.5
Describe the Bug
The validate property is typed the following: validate?: Validate<string | string[], unknown, unknown, TextField>; This and all the others are wrong. It should like other properties depend on if its hasMany: true or false, like its solved for maxRows.
I made a custom field which only supports hasMany: false and i would either have to add multiple TS ignores or add validation logic that cannot be reached.
Reproduction Steps
Add a validate callback on any Field.
Adapters and Plugins
No response
export type TextField = {
admin?: {
autoComplete?: string;
components?: {
Error?: ErrorComponent;
Label?: LabelComponent;
afterInput?: CustomComponent[];
beforeInput?: CustomComponent[];
};
placeholder?: Record<string, string> | string;
rtl?: boolean;
} & Admin;
maxLength?: number;
minLength?: number;
type: 'text';
validate?: Validate<string | string[], unknown, unknown, TextField>;
} & ({
/** Makes this field an ordered array of strings instead of just a single string. */
hasMany: true;
/** Maximum number of strings in the strings array, if `hasMany` is set to true. */
maxRows?: number;
/** Minimum number of strings in the strings array, if `hasMany` is set to true. */
minRows?: number;
} | {
/** Makes this field an ordered array of strings instead of just a single string. */
hasMany?: false | undefined;
/** Maximum number of strings in the strings array, if `hasMany` is set to true. */
maxRows?: undefined;
/** Minimum number of strings in the strings array, if `hasMany` is set to true. */
minRows?: undefined;
}) & FieldBase;
validate should move to the union type related to hasMany
Just confirming that the types for the validate property are still broken in beta.115.
validate should move to the union type related to hasMany
The validate property is now in the union with hasMany, but this doesn't completely solve the underlying problem unfortunately. TypeScript simply cannot infer the validate functions through our Fields[] union. This is what the Collection and Global types use to type their fields property. It struggles to conclusively narrow the types and falls back to any as a result. Here's a simplified example:
type TextField = {
type: 'text';
} & (
| {
hasMany: true;
validate?: (value: string[]) => true | string;
}
| {
hasMany?: false | undefined;
validate?: (value: string) => true | string;
}
);
export type TextareaField = {
maxLength?: number
minLength?: number
type: 'textarea'
validate?: (value: string) => true | string
}
const singleField: TextField = {
type: 'text',
validate: (value) => { // Inferred as string
if (value === 'hello') {
return 'Cannot be hello';
}
return true;
},
};
const multipleField: TextField = {
type: 'text',
hasMany: true,
validate: (value) => { // Inferred as string[]
if (value.length === 0) {
return 'Cannot be empty';
}
return true;
},
};
type Fields = (TextField | TextareaField)[]
const fields: Fields = [
{
type: 'text',
validate: (value) => { // DOES NOT WORK!!!
if (value === 'hello') {
return 'Cannot be hello';
}
return true;
},
}
]
There is likely a different way we can structure this but it's not going to be straightforward. Might need to perform some TypeScript gymnastics here.
Problem is persisting in 3.52.0