Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping a generic TypeScript interface with unknown depth

I have a recursive function that takes an object with a generic interface. The function traverses the object and creates a new object with almost exactly the same interface, except for the end-nodes/leafs, it transforms them into numbers. The function returns this new object, but I cannot find out how to give this the correct type.

Example:

interface IOriginal
{
    prop1: string,
    prop2: number,
    prop3: {
        prop1: boolean,
        prop2: {
            prop1: number
        },
        prop3: string
    }
}

const input : IOriginal = {
    prop1: "somestring",
    prop2: 5,
    prop3: {
        prop1: true,
        prop2: {
            prop1: 2
        },
        prop3: "otherstring"
    }
}

function traverse<T>(obj: T)
{
    /* ... map the object ... */
    return mappedObj
}

const output = traverse(input)

and output's (mapped object's) interface should look like

interface IOutput
{
    prop1: number,
    prop2: number,
    prop3: {
        prop1: number,
        prop2: {
            prop1: number
        },
        prop3: number
    }
}

With mapped types I can only seem to map the first level of depth, if that makes sense.

like image 888
Ruben Avatar asked Mar 06 '23 01:03

Ruben


1 Answers

You can use conditional types to achieve this result:

type LeafsToNumbers<T> = 
    T extends string | number | boolean  ? number :  // If primitive transform to number
    { [ P in keyof T] : T[P] extends (infer U)[] ? LeafsToNumbers<U>[] : LeafsToNumbers<T[P]> }; // Otherwise recursively map, with a special case for arrays.

function traverse<T>(obj: T) : LeafsToNumbers<T>
{
    /* ... map the object ... */
    return null as any;
}
let a: IOriginal;
let o = traverse(a);
o.prop1 //number
o.prop3.prop1 // number
like image 98
Titian Cernicova-Dragomir Avatar answered Apr 13 '23 22:04

Titian Cernicova-Dragomir