(not certain about that title)
I would like to accept arbitrarily nested objects and arrays, and split the primitives in all leaf positions into two, the goal being to handle "dirty" and "pristine" values of any type, while maintaining a uniform navigability through both the original and augmented structure.
A simple example would be like
[1] -> [{dirty: 1, pristine: 1}]
{x:[1]} -> {x: [{dirty: 1, pristine: 1}]}
The type definition is seemingly the easy part. I have the following generic conditional type:
class DirtyPristinePrimitive<T> {
dirty : T; pristine: T;
constructor(v: T) {this.dirty = v; this.pristine = v;}
}
type DirtyPristine<T> = T extends string
? DirtyPristinePrimitive<string>
: T extends Number
? DirtyPristinePrimitive<number>
: T extends boolean
? DirtyPristinePrimitive<boolean>
: T extends null
? DirtyPristinePrimitive<null>
: T extends undefined
? DirtyPristinePrimitive<undefined>
: T extends Array<infer U>
? Array<DirtyPristine<U>>
: {[k in keyof T]: DirtyPristine<T[k]>};
The above appears to work if I explicitly pass in types and introspect them in the IDE, up to a few levels of nesting anyway.
The issue is writing a function to create values of this type from some T. I have:
function dirtyPristine<T>(v: T) : DirtyPristine<T> {
if (Array.isArray(v)) {
return v.map(e => dirtyPristine(e));
}
else if (isObject(v)) {
const result = {};
for (const key of Object.keys(v)) {
(<any>result)[key] = dirtyPristine((<any>v)[key]);
}
return result;
}
else {
return new DirtyPristinePrimitive(v);
}
}
But at all 3 return statements the compiler errors with "Type ... is not assignable to DirtyPristine<T>".
Is this possible?