Search code examples
typescriptgenericsrecursiontypespartial

Recursive type definitions does not seem to work handle generics?


I think this is a bug in Typescript and I filed it as an issue here. I don't expect it to be fixed (at least not soon, so I want to ask you guys, does anyone happen to have an idea for a better solution/work-around than create_1?

Code

type RecursivePartial<T> = {
    [P in keyof T]?: RecursivePartial<T[P]>;
};

type State<T> = { value: T };

function create_1<T>(){

    let _x: RecursivePartial<State<T>>;
    let _y: State<RecursivePartial<T>>;

    _x = _y;
}

function create_2<T>(){
 /*

 */

    let x: RecursivePartial<State<T>>;
    let y: State<T>;

    /*
        Type 'State<T>' is not assignable to type RecursivePartial<State<T>>'.
            Types of property 'value' are incompatible.
                Type 'T' is not assignable to type RecursivePartial<T>[P]>'.
                    Type 'T[string]' is not assignable to type 'RecursivePartial<T[P]>'.
    */

    x = y; 
}

Expected behavior: I had expected the second example to be valid typescript, i.e. State should be assignable to RecursivePartial>. This should be the case as any State would be a partial of it self given T is the same type.

Actual behavior: I get a type error (see above), it seems that the recursive type definition breaks when it encounters a generic?

TS Playground link The code and type error can be confirmed here; ts-playground example


Solution

  • It looks like a bug to me. Workarounds:

    As I noticed in the Github issue, the first and best workaround is probably to turn on the strictNullChecks compiler option. I really recommend turning it on and keeping it on in general, since it is so useful.


    If you don't want to do that, you can always just use a type assertion to tell the compiler that you know the type better than it does. If the compiler is really resistant about doing an assertion, you can pass it through an assertion of any, like so:

    function create_2<T>(){
        let x: RecursivePartial<State<T>>;
        let y: State<T>;
        x = y as any as RecursivePartial<State<T>>; // I know it!
    }
    

    If you don't want to do that, you could change the definition of RecursivePartial<> to the following:

    type RecursivePartial<T> = {
        [P in keyof T]?: T[P] | RecursivePartial<T[P]>;
    };
    

    This is, I believe, effectively the same thing, but the compiler has an easier time seeing that you can always assign a value of type T to a variable of type RecursivePartial<T>.


    Hope that helps. Good luck!