Skip to content

Bug: Recursive generic union type synonyms cause poor compiler performance #10451

Closed
@benjamin-hodgson

Description

@benjamin-hodgson

TypeScript Version: 1.8.10 and 2.1.0-dev.20160818

Code

type T<A> = { a: TSynonym<A> }
          | { b: TSynonym<A> }
          | { c: TSynonym<A> }
          //| { d: TSynonym<A> }
type TSynonym<A> = T<A>

function f<A>(): T<A> {
    return g<A>()
}
function g<A>(): T<A> {
    // return h<A>()
}
function h<A>(): T<A> {

}

Expected behavior:
Type error, in a reasonable length of time (because g and h do not return a value).

Actual behavior:
This example takes over 7 seconds to (fail to) compile on my machine (a new, maxed-out MacBook Pro) and peaks at over 400MB of memory. Uncommenting the fourth line of T's declaration causes the compiler to crash with an out-of-memory error after about a minute. Uncommenting the call to h doubles the compilation time to 14 seconds and increases peak memory usage to over 1GB.

Removing TSynonym (so that the body of T uses T directly), or removing all the generic types, improves the performance significantly.

My guess is that the type-checker is recursively expanding the definitions of T and TSynonym when it tries to type-check the functions, resulting in a very large quantity of nested union types.

Here is the output of tsc with the --diagnostics flag:

Files:               2
Lines:           19030
Nodes:           98142
Identifiers:     35533
Symbols:        354868
Types:          755366
Memory used:   611459K
I/O read:        0.00s
I/O write:       0.00s
Parse time:      0.14s
Bind time:       0.10s
Check time:      6.73s
Emit time:       0.00s
Total time:      6.97s

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already createdFixedA PR has been merged for this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions