/** * This file contains utilities for creating "projection paths" * from an object type, which can be deeply-nested paths * like `images[].asset` */ import { IsAny, IsNever, Primitive, Simplify } from "type-fest"; import { IsNullable, StringKeys, UndefinedToNull, ValueOf } from "./utils"; import { SimplifyUnion, UnionToIntersectionFast } from "./union-to-intersection"; import { CompatibleKeys, CompatiblePick } from "./compatible-types"; /** * These types are ignored when calculating projection paths, * since they're rarely traversed into. * * This interface is extensible, if you want to add your * own shallow types. */ export interface ProjectionPathShallowTypes { "reference blocks": { _type: "reference"; }; "portable text blocks": { _type: "block"; }; } type ShouldBeShallow = CurrentPath extends "" ? false : NonNullable extends ValueOf ? true : false; /** * These simple types are shallow, and do not need to * include any deeper entries. */ type IsSimpleType = IsAny extends true ? true : IsNever extends true ? true : Value extends Primitive ? true : false; /** * Takes a deeply nested object, and returns * a flattened map of all possible GROQ projections, * with their resulting types * * @example * ProjectionPathEntries<{ foo: Array<{ bar: "BAZ" }> }> * Result: * { * foo: Array<{ bar: "BAZ" }>; * foo[]: Array<{ bar: "BAZ" }>; * foo[].bar: Array<"BAZ">; * } */ export type ProjectionPathEntries = IsAny extends true ? Record : Simplify>>>; type _ProjectionPathEntries = IsSimpleType extends true ? never : ShouldBeShallow extends true ? never : CurrentPath extends "@" ? never : Value extends { _type: "slug"; } ? Record, string> : Value extends Array ? Record<`${CurrentPath}[]`, Value> | (_ProjectionPathEntries<`${CurrentPath}[]`, UndefinedToNull> extends infer ChildEntries ? ValuesAsArrays : never) : ValueOf<{ [Key in StringKeys]: (IsArray extends true ? never : Record, UndefinedToNull>) | ValuesAsMaybeNullable<_ProjectionPathEntries, UndefinedToNull>, IsNullable>; }>; type JoinPath = `${CurrentPath}${CurrentPath extends "" ? "" : "."}${Key}`; type ValuesAsArrays = { [P in keyof T]: Array; }; type ValuesAsMaybeNullable = ValuesAreNullable extends false ? T : { [P in keyof T]: null | T[P]; }; type IsArray = IsAny extends true ? false : Array extends T ? true : false; /** * Retrieves all projection paths for the given T */ export type ProjectionPaths = StringKeys>; /** * Retrieves the value yielded by the Path */ export type ProjectionPathValue> = ProjectionPathEntries[Path]; /** * Finds the projection paths of T * that have an output type compatible with TFilterByType */ export type ProjectionPathsByType = StringKeys, TFilterByType>>; /** * Finds the projection path entries of T * that have an output type compatible with TFilterByType */ export type ProjectionPathEntriesByType = CompatiblePick, TFilterByType>; export {};