Closed Basaingeal closed 1 year ago
+1
Got this bug too.
if anyone wants to take a stab at the types here be my guest, I'll get to this but a little short on time right now. It appears to be a TS only issue, the api does work as described.
@jquense i'll have a look.
so for now I am testing this combination:
object({
type: string(),
polyField: object()
.when('type', {
is: 'foo',
then: (schema) =>
schema.shape({
fooField: string().required(),
}),
})
.when('type', {
is: 'bar',
then: (schema) =>
schema.shape({
barField: string().required(),
}),
}),
});
Error is:
TS2349: This expression is not callable.
Each member of the union type '{ <U extends ISchema<any, any, any, any> = ObjectSchema<{}, AnyObject, {}, "">>(builder: ConditionBuilder<ObjectSchema<{}, AnyObject, {}, "">, U>): U; <U extends ISchema<...> = ObjectSchema<...>>(keys: string | string[], builder: ConditionBuilder<...>): U; <UThen extends ISchema<...> = ObjectSchema<...>, UOtherwise ...' has signatures, but none of those signatures are compatible with each other.
Need more help in this problem.
+1 I reverted back to "yup": "^0.32.11",
@jquense Not sure if this is relevant or not:
I get a typescript error when I migrated from v0.32.11
to 1.0.0
The error is:
Overload 1 of 4, '(keys: string | string[], builder: ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>): StringSchema<...>', gave the following error. Argument of type '{ is: (val: any) => boolean; then: StringSchema<string, AnyObject, undefined, "">; otherwise: StringSchema<Maybe<string | undefined>, AnyObject, undefined, "">; }' is not assignable to parameter of type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>'. Object literal may only specify known properties, and 'is' does not exist in type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>'
The code snippet is this:
return myValue.showIf ? yup.string().when(myObject.key, { is: val => val === myValue.showIf, then: yup.string().required(), otherwise: yup.string().notRequired(), })
Type for showIf
is string | number | undefined
Type for myObject is object where it has key as key.
The error is on the is
condition.
Thanks!
@jquense Not sure if this is relevant or not: I get an typescript error when I migrated from
v0.32.11
to1.0.0
The error is:
Overload 1 of 4, '(keys: string | string[], builder: ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>): StringSchema<...>', gave the following error. Argument of type '{ is: (val: any) => boolean; then: StringSchema<string, AnyObject, undefined, "">; otherwise: StringSchema<Maybe<string | undefined>, AnyObject, undefined, "">; }' is not assignable to parameter of type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>'. Object literal may only specify known properties, and 'is' does not exist in type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">, StringSchema<string | undefined, AnyObject, undefined, "">>'
The code snippet is this:
return myValue.showIf ? yup.string().when(myObject.key, { is: val => val === myValue.showIf, then: yup.string().required(), otherwise: yup.string().notRequired(), })
Type for
showIf
isstring | number | undefined
Type for myObject is object where it has key as key.The error is on the
is
condition. Thanks!
+1 i got this error too. Please advise
maybe related to https://github.com/jquense/yup/issues/1899#issuecomment-1438236928 ?
Ok took a look at this, and the crux of the issue is that when
now (correctly) produces a union of schema, one for each branch of the condition. TS has a hard time telling that each return value has a when method that is type compatible with the other members of the union. I will explore this a bit more but in the meantime using a a single when
with the function builder covers all the same cases with a bit more code. e.g.
object()
.when('type', {
is: 'foo',
then: (schema) =>
schema.shape({
fooField: string().required(),
}),
})
.when('type', {
is: 'bar',
then: (schema) =>
schema.shape({
barField: string().required(),
}),
}),
becomes:
object().when('type', (type) => {
if (type === 'foo') {
return schema.shape({
fooField: string().required(),
});
}
if (type === 'bar') {
return schema.shape({
barField: string().required(),
});
}
});
I think at the moment the only thing we can do is add an overload that is less strict but produces less correct types...
Same issue here!
Got the same issue, I had to revert too
Got the same issue with "yup": "^1.0.2" and "typescript": "^5.0.3" the revert to yup version "yup": "^0.32.11" works fine
Got the same issue with yup version of ^1.1.1
No overload matches this call. Overload 1 of 4, '(keys: string | string[], builder: ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">>): StringSchema<string | undefined, AnyObject, undefined, "">', gave the following error. Argument of type '{ is: (activeType: string) => boolean; then: yup.StringSchema<string, yup.AnyObject, undefined, "">; otherwise: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">; }' is not assignable to parameter of type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">>'. Object literal may only specify known properties, and 'is' does not exist in type 'ConditionBuilder<StringSchema<string | undefined, AnyObject, undefined, "">>'
Folks you may have a different issue, like not having updated your when conditions to match the v1 syntax. If you think you have the same issue please provide a reproduction. Saying you have the same issue, when the original issue is closed and fixed, isn't actionable for me.
I just ran into this issue and the fix for me was to update my then
to use the proper new syntax
before
yup
.string()
.nullable()
.notRequired()
.when('PHONE_IS_X_DIGITS', {
is: (val) => val && val.length > 0,
then: yup.string().min( // <--- issue is here; then is not correct syntax for v1
7,
intl.formatMessage({
id: 'PHONE_IS_X_DIGITS',
defaultMessage: 'Phone must be at least 7 digits',
}),
),
}),
after
yup
.string()
.nullable()
.notRequired()
.when('PHONE_IS_X_DIGITS', {
is: (val) => val && val.length > 0,
then: (schema) => // <---- notice here return function
schema.min(
7,
intl.formatMessage({
id: 'PHONE_IS_X_DIGITS',
defaultMessage: 'Phone must be at least 7 digits',
}),
),
})
And the error went away
for those who want a real and complex example : Here is before upgrade version :
import * as Yup from 'yup';
interface optionsType {
id: string;
label: string;
value: string;
}
const formEpischema = Yup.object().shape(
{
attestationType: Yup.object({}).shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
activityType: Yup.object({}).when('attestationType', {
is: (attestationType: optionsType) =>
attestationType.label !== 'Standard',
then: Yup.object({}).shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
otherwise: Yup.object({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
})
}),
shoesType: Yup.array().when(['helmetType', 'otherEquipement'], {
is: (helmetType: optionsType[], otherEquipement: string) =>
!helmetType?.length && !otherEquipement,
then: Yup.array()
.of(
Yup.object().shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
})
)
.min(1)
.required()
}),
shoesCriteria: Yup.object({}).shape({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
}),
shoesSize: Yup.number().min(10).max(55),
helmetType: Yup.array().when(['shoesType', 'otherEquipement'], {
is: (shoesType: optionsType[], otherEquipement: string) =>
!shoesType?.length && !otherEquipement,
then: Yup.array()
.of(
Yup.object().shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
})
)
.min(1)
.required()
}),
otherEquipement: Yup.string()
.min(4)
.when(['shoesType', 'helmetType'], {
is: (shoesType: optionsType[], helmetType: optionsType[]) =>
!shoesType?.length && !helmetType?.length,
then: Yup.string().min(4).required()
}),
isCefri: Yup.boolean().oneOf([true, false]),
dosimeterType: Yup.object({}).when('isCefri', {
is: true,
then: Yup.object({}).shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
otherwise: Yup.object({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
})
}),
dosimeterRef: Yup.string().when('isCefri', {
is: true,
then: Yup.string().min(7).max(7).required(),
otherwise: Yup.string()
})
},
[
['shoesType', 'helmetType'],
['shoesType', 'otherEquipement'],
['helmetType', 'otherEquipement']
]
);
export default formEpischema;
Here is now :
import * as Yup from 'yup';
interface optionsType {
id: string;
label: string;
value: string;
}
const formEpischema = Yup.object().shape({
attestationType: Yup.object({ // change is here 👈
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
activityType: Yup.object().when('attestationType', {
is: (attestationType: optionsType) => attestationType.label !== 'Standard',
then: () => // change is here 👈
Yup.object({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
otherwise: () => // change is here 👈
Yup.object({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
})
}),
shoesType: Yup.array().when(['helmetType', 'otherEquipement'], {
is: (helmetType: optionsType[], otherEquipement: string) => !helmetType?.length && !otherEquipement,
then: () => // change is here 👈
Yup.array()
.of(
Yup.object().shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
})
)
.min(1)
.required()
}),
shoesCriteria: Yup.object({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
}),
shoesSize: Yup.number().min(10).max(55),
helmetType: Yup.array().when(['shoesType', 'otherEquipement'], {
is: (shoesType: optionsType[], otherEquipement: string) => !shoesType?.length && !otherEquipement,
then: () =>
Yup.array()
.of(
Yup.object().shape({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
})
)
.min(1)
.required()
}),
otherEquipement: Yup.string().min(4).when(['shoesType', 'helmetType'], {
is: (shoesType: optionsType[], helmetType: optionsType[]) => !shoesType?.length && !helmetType?.length,
then: () => Yup.string().min(4).required()
}),
isCefri: Yup.boolean().oneOf([true, false]),
dosimeterType: Yup.object().when('isCefri', {
is: true,
then: () =>
Yup.object({
id: Yup.string().required(),
label: Yup.string().required(),
value: Yup.string().required()
}),
otherwise: () =>
Yup.object({
id: Yup.string(),
label: Yup.string(),
value: Yup.string()
})
}),
dosimeterRef: Yup.string().when('isCefri', {
is: true,
then: (schema) => schema.min(7).max(7).required(),
otherwise: () => Yup.string()
})
},
[
['shoesType', 'helmetType'],
['shoesType', 'otherEquipement'],
['helmetType', 'otherEquipement']
]
);
export default formEpischema;
Old version (0.3): The is
, then
, and otherwise
functions of conditional validations accepted objects as arguments.
New version (1): The then and otherwise functions must return a modified schema. They now take empty arrow functions () => ... to return the modified schema.
Old version (0.3): Validation methods such as required()
, min(), max(), etc. were called directly on the fields (for example: Yup.string().required()).
New version (1): Validation methods must be called via the schema (for example: Yup.string().required() becomes Yup.string().required()).
Old version (0.3): The mixed()
method was used to create a schema when the type was not specified.
New version (1): The mixed()
method has been removed. You must specify the type directly when creating the schema (for example: Yup.string()).
Old version (0.3): The validate()
method directly returned validation errors if they existed, otherwise returned undefined.
New version (1): The validate() method returns a promise. You must use await or .then() to get the results.
Old version (0.3): The isValid()
method directly returned true if the validation was successful, otherwise returned false.
New version (1): The isValid() method returns a promise. You must use await or .then() to get the results.
Old version (0.3): The nullable()
and notRequired(
) methods were used to specify that a field could be null or not required.
New version (1): These methods have been removed. To allow a field to be null or not required, you can simply not call any validation method after the field (eg: Yup.string().nullable() just becomes Yup.string()).
New version (1): The oneOf()
method is now used to validate that a value is part of a given set. For example: Yup.string().oneOf(['value1', 'value2']).
These changes reflect some of the important differences between the two versions of Yup.
const addUserValidationSchema = Yup.object().shape({
fullName: Yup.string(),
email: Yup.string().matches(EMAIL_REGEX, {
message: 'Please enter valid email',
}),
password: Yup.string().matches(
PASSWORD_REGEX,
'Password must be 8 chars min, 1 uppercase, 1 lowercase, 1 digit, 1 special symbol.',
),
walletAddress: Yup.string()
.required('Address is required')
.test('invalid-address', 'Address is invalid', (value) =>
isValidAddress(value),
)
.when(['walletAddress'], {
is: (
value: string,
context: { parent: { email: string; password: string } },
) => {},
then: Yup.string().notRequired(),
otherwise: Yup.string().required(),
}),
});
Can anybody have solution for this?
@SSylvain1989 , thanks!
const registerSchema = yup.object().shape({
data_nascimento: yup
.string()
.required("A data de nascimento é obrigatória")
.length(10, "A data de nascimento está incompleta"),
tp_sexo: yup.string().required("Selecione o gênero"),
telefone: yup.string().optional(),
celular: yup
.string()
.required("O celular é obrigatório")
.length(15, "O celular deve ter 15 caracteres"),
estado: yup.string().required("O estado é obrigatório"),
cidade: yup.string().required("A cidade é obrigatória"),
cep: yup
.string()
.required("O CEP é obrigatório")
.length(9, "O CEP deve ter 9 caracteres"),
endereco: yup.string().required("O endereço é obrigatório"),
numero_endereco: yup
.string()
.required("O número da residência é obrigatório"),
bairro: yup.string().required("O bairro é obrigatório"),
complemento: yup.string().optional(),
plano: yup.string().required("Selecione um plano"),
ds_plano: yup.string().when("plano", {
is: "8",
then: (schema) => schema.required("Digite um plano"),
}),
alergia: yup.string().required("Selecione se possui alguma alergia"),
ds_alergia: yup.string().when("alergia", {
is: "S",
then: (schema) => schema.required("Digite a alergia"),
}),
medicamento: yup.string().required("Selecione se toma algum medicamento"),
ds_medicamento: yup.string().when("medicamento", {
is: "S",
then: (schema) => schema.required("Digite o medicamento"),
}),
cirurgia: yup.string().required("Selecione se já realizou alguma cirurgia"),
ds_cirurgia: yup.string().when("cirurgia", {
is: "S",
then: (schema) => schema.required("Digite a cirurgia"),
}),
comorbidade: yup.string().required("Comorbidade é obrigatória"),
ds_comorbidade: yup.string().when("comorbidade", {
is: "21",
then: (schema) => schema.required("Digite a comorbidade"),
}),
});
This is my solution
interface Inputs {
title: string;
startTime: string;
endTime: string;
type: boolean;
}
const schema = yup
.object({
type: yup.boolean(),
title: yup
.string()
.when('type', {
is: true,
then: (schema) => schema.required('Description must be required'),
})
.when('type', {
is: false,
then: (schema) => schema.optional(),
}),
startTime: yup.string().required('Start Time must be required'),
endTime: yup.string().required('End Time must be required'),
})
.required();
companyType: number()
.required('Company Type is required')
.typeError('Company Type must be a number'),
taxOffice: string()
.required('Tax Office is required')
.when('companyType', {
is: (val: number) => val !== 2,
then: (schema) => schema.required('Tax Office is required'),
otherwise: (schema) => schema.notRequired(),
}),
it works for me
Ok took a look at this, and the crux of the issue is that
when
now (correctly) produces a union of schema, one for each branch of the condition. TS has a hard time telling that each return value has a when method that is type compatible with the other members of the union. I will explore this a bit more but in the meantime using a a singlewhen
with the function builder covers all the same cases with a bit more code. e.g.object() .when('type', { is: 'foo', then: (schema) => schema.shape({ fooField: string().required(), }), }) .when('type', { is: 'bar', then: (schema) => schema.shape({ barField: string().required(), }), }),
becomes:
object().when('type', (type) => { if (type === 'foo') { return schema.shape({ fooField: string().required(), }); } if (type === 'bar') { return schema.shape({ barField: string().required(), }); } });
Thank you!
I just ran into this issue and the fix for me was to update my
then
to use the proper new syntaxbefore
yup .string() .nullable() .notRequired() .when('PHONE_IS_X_DIGITS', { is: (val) => val && val.length > 0, then: yup.string().min( // <--- issue is here; then is not correct syntax for v1 7, intl.formatMessage({ id: 'PHONE_IS_X_DIGITS', defaultMessage: 'Phone must be at least 7 digits', }), ), }),
after
yup .string() .nullable() .notRequired() .when('PHONE_IS_X_DIGITS', { is: (val) => val && val.length > 0, then: (schema) => // <---- notice here return function schema.min( 7, intl.formatMessage({ id: 'PHONE_IS_X_DIGITS', defaultMessage: 'Phone must be at least 7 digits', }), ), })
And the error went away
Thanks. It's work for me
Describe the bug When you call
when()
a 2nd time in a schema, TypeScript complains with an arcane type errorTo Reproduce
This is an example schema that used to work before v1.
TypeScript gives this error, concerning the 2nd
when
call for thetimeEarly
field.Expected behavior This worked fine with no type errors in
v0.x
Platform (please complete the following information): TypeScript v4.9.5 yup v1.0.0