Can I add a default value to a type if everything other satisfies in a union type

const type1 = type({name: "string"}) const type2 = type({city: "string"}) const union = type1.or(type2) const result1 = union({name:"ankit"}) const result2 = union({city:"Ajmer"}) I want result1 to be {name: "ankit",kind:"name"} I want result2 to be {city:"Ajmer",kind:"city"} or better const [kind,result] = union({name:"ankit"}) here kind will be "name" and result will be {name:"ankit"} and kind type will be "name"| "city" can I add these default value of kind in the type definitions so that if parsed with that validator from the union the kind is added
33 Replies
Dimava
Dimavaā€¢3w ago
You should be able to union morphs If they have incompatible inputs What if you have {city, name}? šŸ¤” @aabad_ankit
import { type } from 'arktype'

let c = type({city: 'string', '+': 'reject'}).pipe(o => ({...o, type: 'city'}))
let n = type({name: 'string', '+': 'reject'}).pipe(o => ({...o, type: 'name'}))

let cn = type([c, '|', n])

console.log(
cn({city: 'foo'}),
cn({name: 'foo'}),
'\n' + cn({city: 'foo', name: 'foo'}) + '\n',
cn({}) + '',
)
import { type } from 'arktype'

let c = type({city: 'string', '+': 'reject'}).pipe(o => ({...o, type: 'city'}))
let n = type({name: 'string', '+': 'reject'}).pipe(o => ({...o, type: 'name'}))

let cn = type([c, '|', n])

console.log(
cn({city: 'foo'}),
cn({name: 'foo'}),
'\n' + cn({city: 'foo', name: 'foo'}) + '\n',
cn({}) + '',
)
{
city: "foo",
type: "city",
} {
name: "foo",
type: "name",
}
name must be removednull or city must be removednull
city must be a string (was missing) or name must be a string (was missing)
{
city: "foo",
type: "city",
} {
name: "foo",
type: "name",
}
name must be removednull or city must be removednull
city must be a string (was missing) or name must be a string (was missing)
Wait @ArkDavid wtf is removednull
aabad_ankit
aabad_ankitā€¢3w ago
@Dimava the Monoreaper If I try this
const baseType = type({ id: "string" }).pipe((o) => {
return { userId: o.id };
});

const newType = baseType.and({name: "string"})

console.log(newType({name:"ankit",id:"102"}))
const baseType = type({ id: "string" }).pipe((o) => {
return { userId: o.id };
});

const newType = baseType.and({name: "string"})

console.log(newType({name:"ankit",id:"102"}))
1. merge is not available on baseType 2. I am not getting the name in the result, it seems the pipe operation is applied in the end
ssalbdivad
ssalbdivadā€¢3w ago
You can't apply structural operations like merge on a morphed type. For now at least, I'd just create a unions of the inputs then manage the discrimination logic in the pipe implementation from the union
ssalbdivad
ssalbdivadā€¢3w ago
GitHub
How should you .pipe to a type constructed with or? Ā· arktypeio...
I have the following: const objSchema = type({ action: "'scheduled' | 'rescheduled' | 'canceled' | 'changed'", id: "parse.integer", calendarID: &...
ssalbdivad
ssalbdivadā€¢3w ago
It would be theoretically possible to discriminate using strict key presence but it's not implemented yet: https://github.com/arktypeio/arktype/issues/786
aabad_ankit
aabad_ankitā€¢3w ago
I need to learn a lot šŸ™‚ . This library is just awesome.
ssalbdivad
ssalbdivadā€¢3w ago
Thank you šŸ˜Š Working on more docs now!
aabad_ankit
aabad_ankitā€¢3w ago
docs will certainly help. @ArkDavid How can I get better error msg for this
const type1 = type({"name": "string",age:"number"})
const type2 = type({"city": "string"})

const union = type([type1,"|",type2])
const result = union({"age":12})
console.log(result.summary)
const type1 = type({"name": "string",age:"number"})
const type2 = type({"city": "string"})

const union = type([type1,"|",type2])
const result = union({"age":12})
console.log(result.summary)
prints "name must be a string (was missing) or city must be a string (was missing)" since age is already there shouldn't it just say "name must be a string (was missing) "
ssalbdivad
ssalbdivadā€¢3w ago
That would require the issue I mentioned before where key presence could be used as a discriminant If you want total control over how the error messages are handled you could use .narrow and create them yourself If you had a discriminant key like kind: "person" kind: "city" or similar, it would check that first, then give the error message based on the branch it is on It could theoretically do that based on key presence once that issue is implemented Oh I guess in those cases you don't have onUndeclaredKeys at all though So the reason it would give both is because it is true that {city: "foo", age: 12} would also be valid Seems to be missing an "or", will fix that But wait null isn't even allowed
ssalbdivad
ssalbdivadā€¢3w ago
Whoops found it
No description
aabad_ankit
aabad_ankitā€¢3w ago
I am getting name must be a string (was missing) or city must be a string (was missing) for both the code snippets below.
const type1 = type({"name": "string",age:"number","+": "delete"})
const type2 = type({"city": "string","+": "delete"})
const union = type([type1,"|",type2])
const result = union({"age":12})
console.log(result.summary)
const type1 = type({"name": "string",age:"number","+": "delete"})
const type2 = type({"city": "string","+": "delete"})
const union = type([type1,"|",type2])
const result = union({"age":12})
console.log(result.summary)
and
const type1 = type({ name: "string", age: "number", "+": "delete" }).pipe(
(o) => {
return { ...o, kind: "person" as const };
}
);
const type2 = type({ city: "string", "+": "delete" }).pipe((o) => {
return { ...o, kind: "city" as const };
});

const union = type([type1, "|", type2]);
const result = union({ age: 12, kind: "city" });
console.log(result.summary);
const type1 = type({ name: "string", age: "number", "+": "delete" }).pipe(
(o) => {
return { ...o, kind: "person" as const };
}
);
const type2 = type({ city: "string", "+": "delete" }).pipe((o) => {
return { ...o, kind: "city" as const };
});

const union = type([type1, "|", type2]);
const result = union({ age: 12, kind: "city" });
console.log(result.summary);
ssalbdivad
ssalbdivadā€¢3w ago
That is expected, it's validating the input not the output. Adding a kind: "city" after it's already been validated wouldn't change anything
aabad_ankit
aabad_ankitā€¢2w ago
Oh Yeah, Understood šŸ‘ @ArkDavid @Dimava the Monoreaper What will be the most idiomatic way to do the validation for such union types to get correct error msg. Do I need to check for the properties and do validation by creating a validator map
import { type } from "arktype";
import { keys } from "remeda";

const nameArk = type({ name: "string" }).onDeepUndeclaredKey("delete");
const cityArk = type({ city: "string" }).onDeepUndeclaredKey("delete");
const vm = {
"name": nameArk,
"city": cityArk
};
const input = { name: "ark" };
let result: typeof nameArk.infer | typeof cityArk.infer;
for (const key of keys(vm)) {
if (key in input) {
result = vm[key].assert(input);
break;
}
}

// peform pipe operation here
import { type } from "arktype";
import { keys } from "remeda";

const nameArk = type({ name: "string" }).onDeepUndeclaredKey("delete");
const cityArk = type({ city: "string" }).onDeepUndeclaredKey("delete");
const vm = {
"name": nameArk,
"city": cityArk
};
const input = { name: "ark" };
let result: typeof nameArk.infer | typeof cityArk.infer;
for (const key of keys(vm)) {
if (key in input) {
result = vm[key].assert(input);
break;
}
}

// peform pipe operation here
This is getting verbose. I am giving example of just one field but my inputs are larger and I have 5 different types of input. If I could have done union then I would have declared on deepUndeclaredkey("delete") on just the union as well as the pipe on the union
ssalbdivad
ssalbdivadā€¢2w ago
What is the actual problem you are having?
aabad_ankit
aabad_ankitā€¢2w ago
const nameArk = type({ name: "string", age: "number" }).onDeepUndeclaredKey(
"delete"
);
const cityArk = type({ city: "string" }).onDeepUndeclaredKey("delete");
const input = { name: "ark" };
const result = nameArk.or(cityArk)(input)
console.log(result.summary)
const nameArk = type({ name: "string", age: "number" }).onDeepUndeclaredKey(
"delete"
);
const cityArk = type({ city: "string" }).onDeepUndeclaredKey("delete");
const input = { name: "ark" };
const result = nameArk.or(cityArk)(input)
console.log(result.summary)
This prints age must be a number (was missing) or city must be a string (was missing). I just want the error msg to be age must be a number (was missing) to do that I am currently doing something like this
const vm = {
"name": nameArk,
"city": cityArk
};
const input = { name: "ark" };
let result: typeof nameArk.infer | typeof cityArk.infer;
for (const key of keys(vm)) {
if (key in input) {
result = vm[key].assert(input);
break;
}
}
const vm = {
"name": nameArk,
"city": cityArk
};
const input = { name: "ark" };
let result: typeof nameArk.infer | typeof cityArk.infer;
for (const key of keys(vm)) {
if (key in input) {
result = vm[key].assert(input);
break;
}
}
so I wanted to know if there is more idiomatic way to do this
Want results from more Discord servers?
Add your server