Stuart B
Stuart B
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
That's great David, thanks! I think it would be worth clearly mentioning in the docs that defaults refer to the input type, regardless of where you assign them. It's pretty obvious with 'string="off"', but if you add a .default() after the morph, I expect some people will assume that it applies to the morphed type. Also, something like type('string|boolean=false') doesn't work. If there is no value passed then false as a default is not applied. If that's intentional (and I can see why it's a weird thing to do), then should it give a type error in the editor, rather than just ignore it?
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
(and thanks)
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
It is such a relief that it wasn't me being an idiot!
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
Hi @ArkDavid , just to close this off, is the above example the way I need to go with this, or is there an approach that doesn't require 2 preprocess steps? I'm happy with either answer. And sorry for my ignorance, if that's what it is. You're doing a very impressive job with Arktype!
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
I'm still missing something. The below is the simplest example of what I want to do. This works, but it requires me to generate 2 preprocessing types, one to make sure there is a string value present and the second to transform the string value to a Boolean. I can't get my head around why that can't be combined into one preprocessing step.
It seems like adding the 'string="off"' default to the second preprocess type should work, but it just ignores it. And I can't put the logic in the morph, as the morph doesn't run if it doesn't get a string value. Maybe I just have to accept "that's just the way it is, deal with it"!
// This is the shape of the data I need. I might use it for other things as well as form input.
const validator = type({
bool_value: 'boolean'
});

// These two preprocessors are just turning what I get from an HTML form checkbox into boolean
const pre_process_1 = type({
bool_value: type('string="off"')
});
const pre_process_2 = type({
bool_value: type('string').pipe((v) => (v === 'on' ? true : false))
});

const data = {
// bool_value does not exist, so I want it to be false
// if bool_value='on' then I need true. Otherwise false.
};

const preprocess_1_result = pre_process_1(data);
if (preprocess_1_result instanceof type.errors) console.log(preprocess_1_result.summary);
else console.log('preprocess 1:', preprocess_1_result);

const preprocess_2_result = pre_process_2(preprocess_1_result);
if (preprocess_2_result instanceof type.errors) console.log(preprocess_2_result.summary);
else console.log('preprocess 2:', preprocess_2_result);

const result = validator(preprocess_2_result);
if (result instanceof type.errors) console.log(result.summary);
else console.log('final:', result);
// This is the shape of the data I need. I might use it for other things as well as form input.
const validator = type({
bool_value: 'boolean'
});

// These two preprocessors are just turning what I get from an HTML form checkbox into boolean
const pre_process_1 = type({
bool_value: type('string="off"')
});
const pre_process_2 = type({
bool_value: type('string').pipe((v) => (v === 'on' ? true : false))
});

const data = {
// bool_value does not exist, so I want it to be false
// if bool_value='on' then I need true. Otherwise false.
};

const preprocess_1_result = pre_process_1(data);
if (preprocess_1_result instanceof type.errors) console.log(preprocess_1_result.summary);
else console.log('preprocess 1:', preprocess_1_result);

const preprocess_2_result = pre_process_2(preprocess_1_result);
if (preprocess_2_result instanceof type.errors) console.log(preprocess_2_result.summary);
else console.log('preprocess 2:', preprocess_2_result);

const result = validator(preprocess_2_result);
if (result instanceof type.errors) console.log(result.summary);
else console.log('final:', result);
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
It's sort of the opposite of 1089. I can apply the morph ok but the defaults on the initial type get ignored. Is that the same cause?
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
I know it's controversial but I find snake_case easier to read. I agree camelCase somehow looks better and is the js standard, but I find it easier to follow my code using snake_case.
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
So what's happening is that if I don't have a 'is_male' (e.g. a checkbox is not checked) then the default is not being applied to an optional field so I end up with no 'is_male' value in the output, when I need false.
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
I then pass this type through the function to get a modified type that is used to validate form input.
const person = type({
'name?': 'string="Dave"',
'age?': 'number>18=19',
height: 'number<300',
'is_male?': 'boolean=true'
});
const person = type({
'name?': 'string="Dave"',
'age?': 'number>18=19',
height: 'number<300',
'is_male?': 'boolean=true'
});
If I then try to validate this data:
const fake_formData_1 = {
name: 'stuart',
age: '50',
height: '180'
//is_male: ''
};
const fake_formData_1 = {
name: 'stuart',
age: '50',
height: '180'
//is_male: ''
};
I run into a problem with the default. The default for 'name' works ok. 'name' is a string input in both the original and modified types. But the defaults for age and is_male get lost, even though I have pipe the string through original with .pipe(value). That .pipe(value) is retaining the >18 number constraint on age but losing the =19. I've tried adding .default() into the function but it makes no difference. What am I missing?
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
I'm stuck again! (It's getting a bit embarrassing now, sorry David!). This is a section of my current code.
const key_name = key.unit as never;
const value: Type = validator.get(key as never);
const non_optional_value = value.exclude('undefined');
let new_value: Type;
switch (true) {
case non_optional_value.extends(type.number):
console.log('Number field');
new_value = type('string.numeric.parse').pipe(value);
break;
case non_optional_value.extends('boolean'):
console.log('Boolean field');
new_value = type('string')
.pipe((text) => (text === 'on' ? true : false))
.pipe(value);
break;
default:
console.log('any other type of field');
new_value = non_optional_value;
}
const key_name = key.unit as never;
const value: Type = validator.get(key as never);
const non_optional_value = value.exclude('undefined');
let new_value: Type;
switch (true) {
case non_optional_value.extends(type.number):
console.log('Number field');
new_value = type('string.numeric.parse').pipe(value);
break;
case non_optional_value.extends('boolean'):
console.log('Boolean field');
new_value = type('string')
.pipe((text) => (text === 'on' ? true : false))
.pipe(value);
break;
default:
console.log('any other type of field');
new_value = non_optional_value;
}
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
Sounds good. And once again, thanks for the help!
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
Is there an easy(ish) way that I can force any boolean to be optional in the new type? (Because for this use case, it always will be, as I'll receive 'on' or nothing at all)
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
My points/questions: 1. In the examples above in this thread, the "detection" of a Boolean field in the type is done by checking value.extends('boolean'). For some reason I don't understand, that doesn't work here but value.overlaps('boolean') does. My assumption here is that I'm being an idiot, but I can't work out where. 2. In Plat's conform parser I see that value.or('string.numeric.parse') is used for a number field. This works but seems to mean that and numeric constraints that were on the original numeric field definition don't apply when it's parsed from a string. So I tried type('string.numeric.parse').pipe(value), which seems to keen any original numeric constraints. 3. The below works for numbers, but Boolean is causing me problems. The original type has the Boolean as an option (as an unchecked checkbox on a form won't provide a value in formData). But the function is converting the optional key back into required. So it works if a checkbox is checked and passes 'on' but doesn't work if it is unchecked.
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
const person = type({
name: 'string',
age: 'number>18',
height: 'number<300',
'is_male?': 'boolean'
});

const fake_formData_1 = {
// This works
name: 'Stuart',
age: '40',
height: '180',
is_male: 'on'
};

const fake_formData_2 = {
// This errors.
name: 'Stuart',
age: '40',
height: '180'
}; // It seems that the formify function is converting any optional keys into required.
// And I think I'm too dumb to figure out why!

const person_form_validator = formify(person);

const validated_1 = person_form_validator(fake_formData_1);
console.log('validated_1:');
if (validated_1 instanceof ArkErrors) console.log(validated_1.summary);
else console.log(validated_1);

const validated_2 = person_form_validator(fake_formData_2);
console.log('validated_2:');
if (validated_2 instanceof ArkErrors) console.log(validated_2.summary);
else console.log(validated_2);
const person = type({
name: 'string',
age: 'number>18',
height: 'number<300',
'is_male?': 'boolean'
});

const fake_formData_1 = {
// This works
name: 'Stuart',
age: '40',
height: '180',
is_male: 'on'
};

const fake_formData_2 = {
// This errors.
name: 'Stuart',
age: '40',
height: '180'
}; // It seems that the formify function is converting any optional keys into required.
// And I think I'm too dumb to figure out why!

const person_form_validator = formify(person);

const validated_1 = person_form_validator(fake_formData_1);
console.log('validated_1:');
if (validated_1 instanceof ArkErrors) console.log(validated_1.summary);
else console.log(validated_1);

const validated_2 = person_form_validator(fake_formData_2);
console.log('validated_2:');
if (validated_2 instanceof ArkErrors) console.log(validated_2.summary);
else console.log(validated_2);
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
Below is the code I'm working with (and forgive me, I'm not exactly an experienced dev)
function formify<T extends Type<object>>(validator: T): T {
return validator.keyof().internal.distribute(
(key) => {
if (!key.hasKind('unit')) {
throw new Error('Index signatures cannot be mapped');
}
const unit = key.unit as never;
const value: Type = validator.get(key as never);

let new_value: Type;
switch (true) {
case value.extends(type.number):
console.log('Number field');
// This picks up number values in the original type and adds a parse string in front
// Any original constrints on the number are still applied
new_value = type('string.numeric.parse').pipe(value);
break;
case value.overlaps('boolean'):
// NOTE: If I use .extends here it does not pick up the field and the boolean field drops through to default.
// This picks up boolean values, changes to a string and then morphs 'on' back to true.
console.log('Boolean field');
new_value = type('string').pipe((text) => (text === 'on' ? true : false));
break;
default:
console.log('any other type of field');
new_value = value;
}
return [unit, new_value];
},
(entries) => type(Object.fromEntries(entries))
);
}
function formify<T extends Type<object>>(validator: T): T {
return validator.keyof().internal.distribute(
(key) => {
if (!key.hasKind('unit')) {
throw new Error('Index signatures cannot be mapped');
}
const unit = key.unit as never;
const value: Type = validator.get(key as never);

let new_value: Type;
switch (true) {
case value.extends(type.number):
console.log('Number field');
// This picks up number values in the original type and adds a parse string in front
// Any original constrints on the number are still applied
new_value = type('string.numeric.parse').pipe(value);
break;
case value.overlaps('boolean'):
// NOTE: If I use .extends here it does not pick up the field and the boolean field drops through to default.
// This picks up boolean values, changes to a string and then morphs 'on' back to true.
console.log('Boolean field');
new_value = type('string').pipe((text) => (text === 'on' ? true : false));
break;
default:
console.log('any other type of field');
new_value = value;
}
return [unit, new_value];
},
(entries) => type(Object.fromEntries(entries))
);
}
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
If I use value.overlaps('boolean'), it works ok. But I'm not sure if that's wise?
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
Also... I'm a bit puzzled. I can't get the check for a boolean type to work. I have a boolean property in my type but it's not getting picked up by value.extends('boolean') or value.extends(type.boolean). I assume it worked for you?
174 replies
Aarktype
Created by PIat on 9/4/2024 in #questions
Converting Zod to Arktype
@PIat Thanks for pointing me at this. I'm not using conform but this helps me with my problem. One comment... I changed ? value.or('string.numeric.parse') to type('string.numeric.parse').pipe(value). Otherwise, if you have something like {age:'number<100'} and pass it a string like '101', it correcty parses to a number but the >100 constraint is lost.
174 replies
Aarktype
Created by Stuart B on 8/23/2024 in #questions
Automatically applying parse.integer
Did you every get this working?
160 replies
Aarktype
Created by Stuart B on 9/5/2024 in #questions
Any way to tell if a custom description has been provided?
Understood, thanks.
11 replies