Let's say we have a type with some nested properties, like so:
type Deep = {
foo: number;
bar: {
nested1: string;
nested2: number[];
}
}
But we need to pass objects of that type through a system which can't handle nested properties. So we'll flatten the objects, using a separator like _
to denote nested properties:
type Flat = {
foo: number;
bar_nested1: string;
bar_nested2: number[];
}
We can implement functions for flattening and unflattening these objects. But, if we want the functions to be generic, how do we represent their return types?. Is it even possible in Typescript currently (v4.5)?
function flatten<TDeep>(deep: TDeep): Flatten<TDeep>; // How do we define `Flatten<>`?
function unflatten<TFlat>(flat: TFlat): Unflatten<TFlat>; // How do we define `Unflatten<>`?
I had a stab at Unflatten<>
using Typescript 4.5.
type Unflatten<T> = WithoutDeepProps<T> & WithUnflattenedProps<T>
type WithoutDeepProps<T> = {
[Property in keyof T as Exclude<Property, `${string}_${string}`>]: T[Property];
}
type WithUnflattenedProps<T> = {
[Property in keyof T as ParentOf<Property>]: {
[ChildProperty in ChildOf<Property>]: T[Property]
}
}
type ParentOf<T> = T extends `${infer Parent}_${string}` ? Parent : never;
type ChildOf<T> = T extends `${string}_${infer Child}` ? Child : never;
This kind-of works but it unions the nested property types. So, using Flat
from above:
// Unflatten<Flat> gives:
foo: number;
bar: {
nested1: string | number[];
nested2: string | number[];
}
I haven't even tried Flatten<>
because I don't know how I would convert one property (e.g. bar
) into many (bar_nested1
, bar_nested2
)