jsonschema-definer
jsonschema-definer copied to clipboard
Feature: Omit, Pick, and Intersection utility
Would be great if there was an omit
or pick
option for the Object type. Something like
const t1 = s.shape({ id: s.string(), text: s.string(), text2: s.string() })
const t2 = t1.omit("text", "text2");
Added the following to your object.ts file. Not sure how to get the types outputted correctly.
/**
* Return new ObjectSchema with omitted fields
*
* @returns {ObjectSchema}
*/
omit<K extends keyof T>(...keys: K[]) {
const es = new Set(keys as string[]);
const { properties, required, ...plain } = this.valueOf();
const np = {} as ObjectJsonSchema;
for (const key in properties) {
if (!es.has(key)) np[key] = properties[key];
}
(plain.properties as Record<string, unknown>) = np;
if (required) {
const nrs = [];
for (let i = 0; i < required.length; i++) {
const r = required[i];
if (!es.has(r as any)) nrs.push(r);
}
if (nrs.length > 0) (plain.required as string[]) = nrs;
}
return Object.assign(Object.create(this.constructor.prototype), {
...this,
plain,
});
}
/**
* Return new ObjectSchema with picked fields
*
* @returns {ObjectSchema}
*/
pick<K extends keyof T>(...keys: K[]) {
// const plain = {} as Record<string, unknown>;
const { properties = {}, required, ...plain } = this.valueOf();
for (const key of keys) {
(plain[key as string] as Record<string, unknown>) = properties[
key as string
];
}
if (required) {
const nrs = [];
const ins = new Set(keys as string[]);
for (let i = 0; i < required.length; i++) {
const r = required[i];
if (ins.has(r)) nrs.push(r);
}
if (nrs.length > 0) (plain.required as string[]) = nrs;
}
return Object.assign(Object.create(this.constructor.prototype), {
...this,
plain,
});
}
I noticed your ajv
and ts-toolbelt
are old. Probably should upgrade. Also, your ajv
addKeyword
's validate
function is failing typescript.
Added omit and pick functions with correct type outputs. Also fixed partial's output since it wasn't outputting correctly for me.
Added an "intersection" object feature. Identical to the shape found in the index but with some typescript adjustments. Fixed a couple of errors. Having some difficultly with "intersection" type output. Typescript is tough... Can't edit the pull request :(
/**
* Return new ObjectSchema with picked fields
*
* @returns {ObjectSchema}
*/
pick<K extends keyof T>(...keys: K[]): ObjectSchema<Pick<T, K>, R> {
const plain = { ...this.valueOf() };
const { properties, required } = plain;
const nps = {} as ObjectJsonSchema;
if (properties) {
for (const key of keys) {
nps[key as keyof ObjectJsonSchema] =
properties[key as keyof ObjectJsonSchema];
}
plain.properties = nps;
}
if (required) {
const nrs = [];
const ins = new Set(keys as string[]);
for (let i = 0; i < required.length; i++) {
const r = required[i];
if (ins.has(r)) nrs.push(r);
}
if (nrs.length > 0) (plain.required as string[]) = nrs;
}
return Object.assign(Object.create(this.constructor.prototype), {
...this,
plain,
});
}
/**
* Intersections ObjectSchemas
* Return new ObjectSchema with combined fields.
* The new object take precedence for similar properties.
*
* @returns {ObjectSchema}
*/
intersection<A extends Record<string, any>>(
props: ObjectSchema<A, boolean>,
additional = false,
): ObjectSchema<A & T, boolean> {
const res = new ObjectSchema<A & T>()
.additionalProperties(additional)
.copyWith(this as ObjectSchema);
const required = [...(props.plain.required || [])];
for (const prop of this.plain.required || []) {
if (!required.includes(prop)) required.push(prop);
}
return res.copyWith({
plain: {
properties: {
...this.plain.properties,
...props.plain.properties,
},
...(required.length > 0 && {
required,
}),
},
});
}
/**
* Intersections ObjectSchema with Object (like shape)
* Return new ObjectSchema with combined fields.
* The new props take precedence for similar properties.
*
* @returns {ObjectSchema}
*/
intersectionShape<X extends Record<string, BaseSchema<any, boolean>>>(
props: X,
additional = false,
) {
let res = new ObjectSchema<
Optional<{ [Y in keyof X]: X[Y]["otype"] }> & T
>()
.additionalProperties(additional)
.copyWith(this as ObjectSchema);
for (const name in props) {
const schema = props[name];
const { required = [] } = this.plain;
res = res.copyWith({
plain: {
properties: {
...this.plain.properties,
[name]: schema.plain,
},
...(schema.isRequired &&
!required.includes(name) && {
required: [...required, name],
}),
},
});
}
return res;
}
I made many more improvements and since this OP seems to be AFK, created my own repo - https://github.com/TakitoTech/schemez