Skip to content

Incorrect Recursive Union Emmission #31605

@ericwooley

Description

@ericwooley

TypeScript Version: 3.4.5 & typescript@3.5.0-dev.20190525

Search Terms:
Recursive Emit union
Code

export const testRecFun= <T extends Object>(parent: T) => {
    return {
      result: parent,
      deeper: <U extends Object>(child: U) =>
        testRecFun<T & U>({ ...parent, ...child })
    };
}


let p1 = testRecFun({one: '1'})
console.log(p1.result.one)
let p2 = p1.deeper({two: '2'})
console.log(p2.result.one, p2.result.two);
let p3 = p2.deeper({three: '3'})
console.log(p3.result.one, p3.result.two, p3.result.three);

Expected behavior:
each depth would produce a unique generic for U

export declare const testRecFun: <T extends Object>(parent: T) => {
    result: T;
    deeper: <U extends Object>(child: U) => {
        result: T & U;
        deeper: <Ua extends Object>(child: Ua) => {
            result: T & U & Ua;
            deeper: <Ub extends Object>(child: Ub) => {
                result: T & U & Ua & Ub;
                deeper: <Uc extends Object>(child: Uc) => {
                    result: T & U & Ua & Ub & Uc;
                    deeper: <Ud extends Object>(child: Ud) => {
                        result: T & U & Ua & Ub & Uc & Ud;
                        deeper: <Ue extends Object>(child: Ue) => {
                            result: T & U & Ua & Ub & Uc & Ud & Ue;
                            deeper: <Uf extends Object>(child: Uf) => {
                                result: T & U & Ua & Ub & Uc & Ud & Ue & Uf;
                                deeper: <Ug extends Object>(child: Ug) => {
                                    result: T & U & Ua & Ub & Uc & Ud & Ue & Uf & Ug;
                                    deeper: <Uh extends Object>(child: Uh) => {
                                        result: T & U & Ua & Ub & Uc & Ud & Ue & Uf & Ug & Uh;
                                        deeper: <Ui extends Object>(child: Ui) => {
                                            result: T & U & Ua & Ub & Uc & Ud & Ue & Uf & Ug & Uh & Ui;
                                            deeper: <Uj extends Object>(child: Uj) => any;
                                        };
                                    };
                                };
                            };
                        };
                    };
                };
            };
        };
    };
};

Actual behavior:

export declare const testRecFun: <T extends Object>(parent: T) => {
    result: T;
    deeper: <U extends Object>(child: U) => {
        result: T & U;
        deeper: <U extends Object>(child: U) => {
            result: T & U & U;
            deeper: <U extends Object>(child: U) => {
                result: T & U & U & U;
                deeper: <U extends Object>(child: U) => {
                    result: T & U & U & U & U;
                    deeper: <U extends Object>(child: U) => {
                        result: T & U & U & U & U & U;
                        deeper: <U extends Object>(child: U) => {
                            result: T & U & U & U & U & U & U;
                            deeper: <U extends Object>(child: U) => {
                                result: T & U & U & U & U & U & U & U;
                                deeper: <U extends Object>(child: U) => {
                                    result: T & U & U & U & U & U & U & U & U;
                                    deeper: <U extends Object>(child: U) => {
                                        result: T & U & U & U & U & U & U & U & U & U;
                                        deeper: <U extends Object>(child: U) => {
                                            result: T & U & U & U & U & U & U & U & U & U & U;
                                            deeper: <U extends Object>(child: U) => any;
                                        };
                                    };
                                };
                            };
                        };
                    };
                };
            };
        };
    };
};

// intermediate steps are lost.
let p1 = testRecFun({one: '1'})
console.log(p1.result.one)
let p2 = p1.deeper({two: '2'})
console.log(p2.result.one, p2.result.two);
let p3 = p2.deeper({three: '3'})
console.log(p3.result.one, p3.result.two, p3.result.three) // error p3.result.two is not ...

Playground Link:

I didn't make this in the playground, but I made a repo for reproduction.
https://github.com/ericwooley/ts-recursive-test

Related Issues:

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions