How to mark all fields in a type as optional. #835
-
In zod, there was option of .deepPartial() that basically makes all the fields as optional. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 7 replies
-
@dhruv-nudge Hi, TypeBox doesn't provide a built in deepPartial method (as there is no equivalent TS utility type). In TypeScript however, the expectation is generally to leverage the type system programmatically produce an appropriate mapped type. TypeBox follows a similar reasoning, but is a bit more complex as you will need to map both runtime and static type. The following programmatically implements a PartialDeep type. import { TypeGuard, Type, TSchema, TIntersect, TUnion, TObject, TPartial, TProperties, Evaluate } from '@sinclair/typebox'
// -------------------------------------------------------------------------------------
// TPartialDeepProperties
// -------------------------------------------------------------------------------------
export type TPartialDeepProperties<T extends TProperties> = {
[K in keyof T]: TPartialDeep<T[K]>
}
function PartialDeepProperties<T extends TProperties>(properties: T): TPartialDeepProperties<T> {
return Object.getOwnPropertyNames(properties).reduce((acc, key) => {
return { ...acc, [key]: PartialDeep(properties[key]) }
}, {}) as never
}
// -------------------------------------------------------------------------------------
// TPartialDeepRest
// -------------------------------------------------------------------------------------
export type TPartialDeepRest<T extends TSchema[], Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TPartialDeepRest<R, [...Acc, TPartialDeep<L>]>
: Acc
)
function PartialDeepRest<T extends TSchema[]>(rest: [...T]): TPartialDeepRest<T> {
return rest.map(schema => PartialDeep(schema)) as never
}
// -------------------------------------------------------------------------------------
// TPartialDeep
// -------------------------------------------------------------------------------------
export type TPartialDeep<T extends TSchema> =
T extends TIntersect<infer S> ? TIntersect<TPartialDeepRest<S>> :
T extends TUnion<infer S> ? TUnion<TPartialDeepRest<S>> :
T extends TObject<infer S> ? TPartial<TObject<Evaluate<TPartialDeepProperties<S>>>> :
T
export function PartialDeep<T extends TSchema>(schema: T): TPartialDeep<T> {
return (
TypeGuard.IsIntersect(schema) ? Type.Intersect(PartialDeepRest(schema.allOf)) :
TypeGuard.IsUnion(schema) ? Type.Union(PartialDeepRest(schema.anyOf)) :
TypeGuard.IsObject(schema) ? Type.Partial(Type.Object(PartialDeepProperties(schema.properties))) :
schema
) as never
}
// -------------------------------------------------------------------------------------
// Usage
// -------------------------------------------------------------------------------------
const A = PartialDeep(Type.Object({ // single depth
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
}))
const B = PartialDeep(Type.Object({ // multiple depth
a: Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
}),
b: Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
})) TypeBox tries to keep runtime and static implementations somewhat symmetric (so, taking note of the similarities between the TS types and associated mapping functions). You should be able to copy and paste the above type into your project (and modify as required) to get a deepPartial. Hope this helps |
Beta Was this translation helpful? Give feedback.
@dhruv-nudge Hi,
TypeBox doesn't provide a built in deepPartial method (as there is no equivalent TS utility type). In TypeScript however, the expectation is generally to leverage the type system programmatically produce an appropriate mapped type. TypeBox follows a similar reasoning, but is a bit more complex as you will need to map both runtime and static type.
The following programmatically implements a PartialDeep type.
TypeScript Link Here