this string has no description
0
maybefy.ts
108 lines 4.0 kB view raw
1import Maybe, { just, nothing } from "true-myth/maybe"; 2import * as v from "@atcute/lexicons/validations"; 3 4type IsUnion<T, U = T> = T extends unknown ? ([U] extends [T] ? false : true) : never; 5 6type MaybeifyUnion<T> = T extends unknown ? (T extends object ? Maybeify<T> : T) : never; 7 8export type Maybeify<T> = { 9 [K in keyof T]-?: undefined extends T[K] 10 ? T[K] extends Array<infer U> | undefined 11 ? Maybe<Maybeify<U>[]> 12 : IsUnion<NonNullable<T[K]>> extends true 13 ? Maybe<MaybeifyUnion<NonNullable<T[K]>>> 14 : T[K] extends object | undefined 15 ? Maybe<Maybeify<NonNullable<T[K]>>> 16 : Maybe<NonNullable<T[K]>> 17 : T[K] extends Array<infer U> 18 ? Maybeify<U>[] 19 : IsUnion<T[K]> extends true 20 ? MaybeifyUnion<T[K]> 21 : T[K] extends object 22 ? Maybeify<T[K]> 23 : T[K]; 24}; 25 26type AnySchema = v.BaseSchema; 27 28function resolveMemberSchema( 29 member: v.ObjectSchema | v.RecordSchema<v.ObjectSchema, v.RecordKeySchema>, 30): v.ObjectSchema { 31 return (member as { type: string }).type === "record" 32 ? (member as v.RecordSchema<v.ObjectSchema, v.RecordKeySchema>).object 33 : (member as v.ObjectSchema); 34} 35 36function resolveLiteralFromShape(shape: Record<string, AnySchema>): string | undefined { 37 const typeSchema = shape["$type"] as AnySchema | undefined; 38 if (typeSchema === undefined) { 39 return undefined; 40 } 41 const inner = 42 (typeSchema as { type: string }).type === "optional" 43 ? (typeSchema as v.OptionalSchema<AnySchema, unknown>).wrapped 44 : typeSchema; 45 if ((inner as { type: string }).type === "literal") { 46 return (inner as v.LiteralSchema<string>).expected; 47 } 48 return undefined; 49} 50 51export function maybeify<TSchema extends AnySchema>( 52 schema: TSchema, 53 value: v.InferOutput<TSchema>, 54): Maybeify<v.InferOutput<TSchema>> { 55 const schemaType = (schema as { type: string }).type; 56 57 if (schemaType === "optional") { 58 const wrapped = (schema as unknown as v.OptionalSchema<AnySchema, unknown>).wrapped; 59 if (value === undefined) { 60 return nothing() as unknown as Maybeify<v.InferOutput<TSchema>>; 61 } 62 return just(maybeify(wrapped, value as never)) as unknown as Maybeify<v.InferOutput<TSchema>>; 63 } 64 65 if (schemaType === "record") { 66 const objectSchema = (schema as unknown as v.RecordSchema<v.ObjectSchema, v.RecordKeySchema>) 67 .object; 68 return maybeify(objectSchema, value as never) as unknown as Maybeify<v.InferOutput<TSchema>>; 69 } 70 71 if (schemaType === "object") { 72 const objectSchema = schema as unknown as v.ObjectSchema<Record<string, AnySchema>>; 73 const result: Record<string, unknown> = {}; 74 for (const [key, propSchema] of Object.entries(objectSchema.shape)) { 75 const propValue = (value as Record<string, unknown>)[key]; 76 result[key] = maybeify(propSchema as AnySchema, propValue as never); 77 } 78 return result as unknown as Maybeify<v.InferOutput<TSchema>>; 79 } 80 81 if (schemaType === "array") { 82 const arraySchema = schema as unknown as v.ArraySchema<AnySchema>; 83 return (value as unknown[]).map((item) => 84 maybeify(arraySchema.item, item as never), 85 ) as unknown as Maybeify<v.InferOutput<TSchema>>; 86 } 87 88 if (schemaType === "variant") { 89 const variantSchema = schema as unknown as v.VariantSchema<any, any>; 90 const typeValue = (value as Record<string, unknown>)["$type"] as string | undefined; 91 if (typeValue !== undefined) { 92 for (const member of variantSchema.members) { 93 const memberSchema = resolveMemberSchema(member); 94 const expectedType = resolveLiteralFromShape( 95 memberSchema.shape as Record<string, AnySchema>, 96 ); 97 if (expectedType === typeValue) { 98 return maybeify(memberSchema, value as never) as unknown as Maybeify< 99 v.InferOutput<TSchema> 100 >; 101 } 102 } 103 } 104 return value as unknown as Maybeify<v.InferOutput<TSchema>>; 105 } 106 107 return value as unknown as Maybeify<v.InferOutput<TSchema>>; 108}