diff --git a/src/types/generated/utility.ts b/src/types/generated/utility.ts index 64c895c4..327d5607 100644 --- a/src/types/generated/utility.ts +++ b/src/types/generated/utility.ts @@ -1,5 +1,5 @@ /* GENERATED BY https://github.com/Birkbjo/dhis2-open-api-ts */ -import { IdentifiableObject, GistPager, Pager } from './' +import { IdentifiableObject, GistPager, Pager, DataElement, Filter } from './' // import { CategoryCombo, DataElement } from "../generated"; export type ModelCollection = Array @@ -224,3 +224,81 @@ export type PickInModelReferences< Model[P] : Model[P] } + +type Last = T extends [...infer _, infer L] ? L : never +type RemoveLast = T extends [...infer R, any] ? R : never +type RemoveSpaces = S extends `${infer T} ${infer U}` + ? RemoveSpaces<`${T}${U}`> + : S +/* + Helper Type to split a nested field string into components. + NOTE: This only supports one level of nesting. + However, this is a helper, and full recursion support of field-filters + is supported in cooperation with ModelFromFields below. + Split<'categoryOptions,id,name,sharing[public,owner]', ','> + becomes: ["categoryOptions", "id", "name", "sharing[public,owner]"] +*/ +type RecursiveSplitFieldFilter< + S extends string, + D extends string = ',' +> = string extends S + ? string[] + : S extends '' + ? [] + : S extends `${infer T}${D}${infer U}` + ? U extends `${infer RM}[${infer NM}]` + ? // if last part (U) is a nested field eg. categoryCombo[id,name] + // we dont want to split inside of brackets - because that would split the nested field + // eg. "categoryOptions,id,name,sharing[public,owner]" would become ['categoryOptions', 'id', 'name','sharing[public', 'owner]'] + // so we first recurse without last part (sharing) and split the parts before the brackets. + // then we need to add the last element (sharing) back before the brackets. + // Since there's no way to "store" the result of the recursion, we need to do it twice + [ + T, + ...RemoveLast>, + `${Last>}[${NM}]` + ] + : [T, ...RecursiveSplitFieldFilter] + : [S] + +type SplitFieldFilter< + S extends string, + D extends string = ',' +> = RecursiveSplitFieldFilter, D> + +// helper to ensure that a key is a key of an object, if not return never +// mainly to prevent super deep ternaries +type EnsureKeyOf = Key extends keyof Obj ? Key : never + +// gets the model, if in an array, return the model in the array +type GetModel = T extends Array ? U : T + +type MaybeCollection = FullModel extends Array + ? Model[] + : Model + +export type PickModelFields< + Model, + Fields extends string = keyof Model & string // = [...keyof (T & string)] +> = { + [Field in Fields as Field extends `${infer Nested}[${string}]` // Field field is nested + ? EnsureKeyOf // ensure that Nested is a key of T, and return the key (eg. categoryCombo from categoryCombo[id,name]) + : EnsureKeyOf< + Field, + Model + >]: Field extends `${infer Nested}[${infer Rest}]` + ? Nested extends keyof Model // needed to do T[Nested] below + ? // if its a valid nested field, recurse down the object + // wrap in collection, if the original field was a collection + MaybeCollection< + PickModelFields< + GetModel, // get the model from the array, if its an array + SplitFieldFilter[number] // split the rest of the field-filter into ['id', 'name'] + >, + Model[Nested] + > + : unknown // if Nested is not keyOf model, return unknown + : Field extends keyof Model + ? Model[Field] // if its not a nested field, return the property as is + : unknown // not a key of Model, return unknown +}