Recursive conditional types

后端 未结 3 817
走了就别回头了
走了就别回头了 2021-01-22 04:22

I\'d like to map an object recursively so that the primitive values in the object are converted to some other type.

For example, I\'d like an object like this:

相关标签:
3条回答
  • 2021-01-22 04:55

    changing the type to

    type ConvertToTest<T> = {
        [P in keyof T]: T[P] extends any[]
            ? ConvertToTest<T[P]>
            : T[P] extends string
                ? Test
                : ConvertToTest<T[P]>
    }
    

    (extends string instead of extends {}) or

    type ConvertToTest<T> = {
        [P in keyof T]: T[P] extends any[]
            ? ConvertToTest<T[P]>
            : T[P] extends object
                ? ConvertToTest<T[P]>
                : Test    
    }
    

    (extends object instead of extends {})

    seems to do the trick.

    0 讨论(0)
  • 2021-01-22 04:59

    So you need two things from the ConvertToTest<T> type. One is that if T is a primitive type, then CovertToTest<T> = Test. The other is that if T isn't primitive, you want to keep the same keys but convert their values.

    To do that, I'd just add the first case as one part of a conditional type, and then have the other branch use a recursive mapped type:

    type Primitive = string | number | boolean | null | undefined;
    type ConvertToTest<T> = T extends Primitive ? Test : {
        [K in keyof T]:
            T[K] extends (infer U)[] ? ConvertToTest<U>[] :
            ConvertToTest<T[K]>;
    }
    

    Using that, you can then use it like so:

    // For example. Replace with whatever your actual type is.
    type test = {
        foo(): string;
    }
    
    declare function convertToTest<T>(obj: T): ConvertToTest<T>;
    const test = convertToTest({ a: "", b: { c: true, primArr: [1, ""], objArr: [{inner: ""}] } });
    
    test.a.foo(); // OK
    test.b.c.foo(); // OK
    test.b.primArr[0].foo() // OK
    test.b.objArr[0].inner.foo() // OK
    

    This is a nice way to do it since it will work for objects of any depth, and will properly handle converting the elements of an array type as well.

    0 讨论(0)
  • 2021-01-22 05:13

    You were really close. The problem here is that string is assignable to {}.

    Demonstration of this fact here.

    If you check for the string, number, etc. first before {}, then you can get what you want:

    type ConvertToTest<T> = {
        [P in keyof T]: T[P] extends any[]
            ? ConvertToTest<T[P]>
            : T[P] extends string
            ? Test
            : T[P] extends number
            ? Test
            : T[P] extends boolean
            ? Test
            : ConvertToTest<T[P]>
    }
    
    0 讨论(0)
提交回复
热议问题