Replies: 4 comments 1 reply
-
Hey Phillip! Glad to hear you're enjoying the library so far. You could use a combination of There is actually a more powerful API that is not yet documented but allows arbitrary transformation of props called I plan to document it soon as I consider it stable at this point, but here's an example of how you can use it: const original = type({
id: "string", // Key field- should stay the same
name: "string", // Required field
"favoriteColor?": "string" // Optional field
})
const updateSchema = original.map(prop =>
prop.key === "name" ?
{
...prop,
// update key name if needed
key: `prefix${prop.key}` as const,
// update optionality if needed
kind: "optional",
// update value if needed
value: prop.value.or(type.null)
}
: prop
)
type Result = {
id: string
favoriteColor?: string
prefixname?: string | null
} So you could use it so specify all the names of the keys you want to transform like |
Beta Was this translation helpful? Give feedback.
-
Woah... An answer from THE Arktype wizard! ;) Thanks so much @ssalbdivad! :) I wrapped this into a common function and used it across my codebase helping me remove a lot of duplication in the schema definitions! Doing this required an import on some internal types. I saw that there is also an arkKeyOf type that would allow me to avoid the "as any" cast, but that import isn't even findable with the VSCode Intelli-sense viewer, so I just stuck with this for now. import { type } from "arktype";
import { ObjectType } from "arktype/internal/methods/object.ts";
export function generateUpdateSchema<T extends object>(schema: ObjectType<T>, keys: (keyof T)[] = []) {
return schema.omit(...(keys as any)).map((prop) => ({
...prop,
kind: "optional",
value: prop.value.or(type.null),
}));
} |
Beta Was this translation helpful? Give feedback.
-
Oops, I spoke too soon. I can get the code working if I use it inline (and let typescript do all of the inference).generic function for it: export const updateTask = task.omit("userId", "id", "date").map((prop) => ({
...prop,
kind: "optional",
value: prop.value.or(type.null),
}));
export type UpdateType = typeof updateType.infer;
// Works - UpdateTask has the proper type But if I try to put the logic into a generic function, the type inference does not work: export function generateUpdateSchema<T extends object>(schema: ObjectType<T>, keys: (keyof T)[] = []) {
return schema.omit(...(keys as any)).map((prop) => ({
...prop,
kind: "optional",
value: prop.value.or(type.null),
}));
}
export const updateTask = generateUpdateSchema(task, ["userId", "id", "date"]);
export type UpdateType = typeof updateType.infer;
// Does not work - UpdateTask has the type {} I suspect the problem is with the type parameters (especially with the keys), but I can't find the right combination of documented (or undocumented) types to lead to the proper inference. Having the small function inline is still much better than what I had before (manually duplicating and modifying all of the field definitions), but if it's easy enough to wrap that into an abstracted function in the future, that would be 100%. Thanks! |
Beta Was this translation helpful? Give feedback.
-
Ooops, spoke too soon again. :/ Sorry it looks like the return type of each prop is only hitting one of the discriminated union types. So every property has the same type as one of the fields, but not the one that belonged to it. I'm going to revert back to my hack for now (defining the update schema manually) and look towards any future changes that might help this. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello Arktype wizards,
I have only been using this library for a few days now, but I'm pretty happy with it so far. I'm running into one trouble-spot and after combing through the docs and I've decided that I'm definitely not smart enough to figure this one out on my own, so I'm reaching out for some help. Hopefully this is a quick-and-easy few lines for someone.
I have some schemas that are being used as data-transfer objects between my client and my API. These objects have some key fields (which are of course required), and then a mixture of required and optional properties. I want to support update operations via PATCH in the API so that clients can submit a subset of the fields to be updated. Any fields that are not passed in are left alone and not modified in the backend. In order to unset a property, a value of null needs to be passed into the PATCH operation. null is not a valid value for the fields normally.
So what I'm looking for is a generic way to take my existing schema, identify a few properties that should be left as required from the original (the key fields), and then all other fields are updated so that required fields become optional (similar to Partial<>), and optional fields become nullable.
Here is an example:
const original = {
id: "string", // Key field- should stay the same
name: "string", // Required field
"favoriteColor?": "string" // Optional field
}
const updateSchema = NullablePartial(original, "id") ;
/* updateSchema:
{
id: "string",
"name?": "string"
"favoriteColor?": "string | null"
}
*/
This way I can pass {id: "abc123", "name": "Gandalf"} to change the name and leave the color alone. Or {id: "abc123", "color": null } to unset the color, or any other combination of the fields, but name cannot be set to null since it is required on the original.
I read through the docs, the community, and even the unit-tests for the Hkts, but I got a little lost. Any help is appreciated. Right now, I've temporarily worked around it by just declaring the second schema manually, but I'd love to generate it from the source. Alternatively, I'm open to the idea that this is just a bad design for how to do partial updates and that I should be doing something completely different, but it works very effectively in my case. I just don't want to repeat the list of properties in two schemas and I want the clients to be able to make partial updates so they can patch fields they know about, but not be concerned about round tripping every other field.
Thanks,
Phillip
Beta Was this translation helpful? Give feedback.
All reactions