import { sure, good, bad } from './core.js' import type { Sure, InferGood, InferBad, MetaNever, MetaObj } from './core.js' import { KVPair, Objectify, Prettify, Unionize } from './utils.js' type PickOptionalsGood> = T extends { v: Sure< unknown, unknown, any, MetaObj<{ type: 'optional' }> > } ? { k: T['k']; v: InferGood } : never type PickNonOptionals> = T extends { v: Sure< unknown, unknown, any, MetaObj<{ type: 'optional' }> > } ? never : { k: T['k']; v: InferGood } export type InferSchemaGood> = Prettify< Partial>>> & Objectify>> > /** Necessary because `typeof x` is not a type guard. */ export function isObject(x: unknown): x is Record { return typeof x === 'object' && x !== null } /** * Makes a object property `optional` * It doesn't make it nullable or undefinedable * * The `optional` function will be checked `only` by the `object` function. * In all other cases it will the value will not be perceived as optional. */ export function optional>( schema: TSchema ): Sure< InferBad, InferGood, unknown, MetaObj<{ type: 'optional' schema: TSchema }> > { // IMPORTANT: It's important to pass a new function here // since `sure` will update the function with the meta // @ts-expect-error return sure(value => schema(value), { type: 'optional', schema, }) } export function object< // TPropFail, TPropGood, TSchema extends Record>, >( schema: TSchema ): Sure< { [K in keyof TSchema & string]?: InferBad }, InferSchemaGood, unknown, MetaObj<{ type: 'object' schema: TSchema }> > { // @ts-expect-error - TODO: expected return sure( value => { if (!isObject(value)) { return bad({}) } const groupFail = {} const groupGood = {} for (const [key, sureFunction] of Object.entries(schema)) { const isOptional = isObject(sureFunction.meta) && sureFunction.meta.type === 'optional' if (isOptional && !(key in value)) { continue } const [good, unsure] = sureFunction(value[key]) if (good) { // @ts-expect-error groupGood[key] = unsure } else { // @ts-expect-error groupFail[key] = unsure } } if (Object.keys(groupFail).length) { return bad(groupFail) } return good(groupGood) }, { type: 'object', schema, } ) }