Skip to content

Performance regression in react-hook-form from 4.4 to 4.5 #46948

Closed as not planned
@andrewbranch

Description

@andrewbranch

I'm experiencing the same issue. With 4.5.2 it takes 2 minutes to simply load a type hint on hover (Controller from react-hook-form in this particular case). Our project is indeed huge, but the same operation with vs code's built-in 4.4.3 is instant.

I have no idea what's happening under the hood, but these are the two things from the server log that look very slow in 4.52:

  • "command":"geterr" - almost two minutes until "event":"requestCompleted" against ~10ms for 4.4.3
  • 44::encodedSemanticClassifications-full: elapsed time (in milliseconds) 109280.8950 against less than 1s first time and less than 10ms later for 4.4.3
Part of TS 4.5.2 server log
Info 2001 [19:47:33.826] request:
    {"seq":41,"type":"request","command":"geterr","arguments":{"delay":0,"files":["/Users/aleksei.tsikov/git/project/src/Component.tsx"]}}
Perf 2002 [19:47:33.827] 41::geterr: async elapsed time (in milliseconds) 0.3917
Info 2003 [19:47:33.828] event:
    {"seq":0,"type":"event","event":"syntaxDiag","body":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","diagnostics":[]}}
Info 2004 [19:49:29.994] event:
    {"seq":0,"type":"event","event":"requestCompleted","body":{"request_seq":41}}
Info 2005 [19:49:29.996] request:
    {"seq":42,"type":"request","command":"getApplicableRefactors","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","startLine":1,"startOffset":1,"endLine":1,"endOffset":1}}
Perf 2006 [19:49:30.009] 42::getApplicableRefactors: elapsed time (in milliseconds) 13.2598
Info 2007 [19:49:30.009] response:
    {"seq":0,"type":"response","command":"getApplicableRefactors","request_seq":42,"success":true,"body":[{"name":"Convert export","description":"Convert default export to named export","actions":[{"name":"Convert default export to named export","description":"Convert default export to named export","kind":"refactor.rewrite.export.named","notApplicableReason":"Could not find export statement"},{"name":"Convert named export to default export","description":"Convert named export to default export","kind":"refactor.rewrite.export.default","notApplicableReason":"Could not find export statement"}]},{"name":"Convert import","description":"Convert namespace import to named imports","actions":[{"name":"Convert namespace import to named imports","description":"Convert namespace import to named imports","kind":"refactor.rewrite.import.named","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Convert import","description":"Convert named imports to namespace import","actions":[{"name":"Convert named imports to namespace import","description":"Convert named imports to namespace import","kind":"refactor.rewrite.import.namespace","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Extract Symbol","description":"Extract function","actions":[{"name":"Extract Function","description":"Extract function","kind":"refactor.extract.function","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract Symbol","description":"Extract constant","actions":[{"name":"Extract Constant","description":"Extract constant","kind":"refactor.extract.constant","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract type","description":"Extract type","actions":[{"name":"Extract to typedef","description":"Extract to typedef","kind":"refactor.extract.typedef","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to type alias","description":"Extract to type alias","kind":"refactor.extract.type","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to interface","description":"Extract to interface","kind":"refactor.extract.interface","notApplicableReason":"Selection is not a valid type node"}]},{"name":"Move to a new file","description":"Move to a new file","actions":[{"name":"Move to a new file","description":"Move to a new file","kind":"refactor.move.newFile","notApplicableReason":"Selection is not a valid statement or statements"}]},{"name":"Add or remove braces in an arrow function","description":"Add or remove braces in an arrow function","actions":[{"name":"Add braces to arrow function","description":"Add braces to arrow function","kind":"refactor.rewrite.arrow.braces.add","notApplicableReason":"Could not find a containing arrow function"},{"name":"Remove braces from arrow function","description":"Remove braces from arrow function","kind":"refactor.rewrite.arrow.braces.remove","notApplicableReason":"Could not find a containing arrow function"}]},{"name":"Convert to template string","description":"Convert to template string","actions":[{"name":"Convert to template string","description":"Convert to template string","kind":"refactor.rewrite.string","notApplicableReason":"Can only convert string concatenation"}]},{"name":"Infer function return type","description":"Infer function return type","actions":[{"name":"Infer function return type","description":"Infer function return type","kind":"refactor.rewrite.function.returnType","notApplicableReason":"Return type must be inferred from a function"}]}]}
Info 2008 [19:49:30.009] request:
    {"seq":43,"type":"request","command":"projectInfo","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","needFileNameList":false}}
Perf 2009 [19:49:30.009] 43::projectInfo: elapsed time (in milliseconds) 0.1021
Info 2010 [19:49:30.009] response:
    {"seq":0,"type":"response","command":"projectInfo","request_seq":43,"success":true,"body":{"configFileName":"/Users/aleksei.tsikov/git/project/tsconfig.json","languageServiceDisabled":false}}
Info 2011 [19:49:30.011] request:
    {"seq":44,"type":"request","command":"encodedSemanticClassifications-full","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","start":1277,"length":3642,"format":"2020"}}
Perf 2012 [19:51:19.295] 44::encodedSemanticClassifications-full: elapsed time (in milliseconds) 109280.8950
Info 2013 [19:51:19.295] response:
    {"seq":0,"type":"response","command":"encodedSemanticClassifications-full","request_seq":44,"success":true,"body":{"spans":[1281,8,2561,1312,6,2561,1342,8,2561,1368,6,2561,1403,29,2825,1436,11,1793,1449,11,1536,1463,17,1544,1496,11,1792,1678,11,1792,1702,5,1537,1712,5,2561,1720,4,1536,1728,15,2561,1769,16,2057,1787,3,1536,1791,5,1536,1803,5,1793,1810,15,1793,1844,5,2857,1851,12,2857,1865,7,2089,1874,8,2857,1887,7,2816,1895,10,1536,1913,13,2561,1936,9,2561,1947,15,1792,1970,8,2561,1980,8,2816,1989,10,2816,2004,4,272,2023,6,2561,2031,10,2816,2046,4,272,2066,4,2561,2100,11,2560,2113,5,2857,2120,4,2089,2126,6,2089,2137,11,2816,2150,6,1793,2158,10,1536,2177,3,2056,2181,4,2560,2186,23,3072,2218,9,2561,2229,6,1792,2236,9,2560,2253,8,2561,2263,6,1792,2270,8,2560,2279,7,3088,2296,6,2561,2304,6,1792,2311,6,2560,2318,7,3088,2350,12,2089,2365,4,2088,2371,4,2560,2393,11,2089,2406,19,2089,2430,13,2816,2444,12,2088,2467,11,2089,2481,7,2816,2501,5,1792,2514,3,3088,2518,8,1793,2533,5,2561,2540,8,1792,2549,3,2560,2554,5,2561,2561,8,1792,2570,5,2560,2586,5,1792,2607,9,2089,2619,5,2856,2646,8,2089,2657,5,2856,2800,15,1792,2868,7,2088,2933,5,1793,3044,11,2088,3288,7,2088,3348,5,1793,3475,5,1792,3481,5,2560,3514,5,1793,3549,8,2089,3560,10,2816,3571,5,1792,3598,5,1792,3604,8,3072,3613,8,2088,3641,8,2856,3660,7,2816,3668,8,2088,3789,7,2088,3847,5,1793,3999,6,2561,4007,8,2088,4035,5,2561,4042,8,2816,4051,8,2088,4107,5,1792,4113,5,2560,4146,5,1793,4175,5,1792,4181,8,3072,4190,10,2816,4201,5,1792,4381,12,2856,4394,10,1793,4408,5,2856,4414,10,1792,4475,9,2088,4618,29,2824,4648,6,2088,4673,11,2088,4705,7,2056,4754,6,2088,4865,19,2088],"endOfLineState":0}}
Info 2014 [19:51:19.297] request:
    {"seq":47,"type":"request","command":"provideInlayHints","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","start":714,"length":4205}}
Perf 2015 [19:51:19.350] 47::provideInlayHints: elapsed time (in milliseconds) 52.9589
Info 2016 [19:51:19.350] response:
    {"seq":0,"type":"response","command":"provideInlayHints","request_seq":47,"success":true,"body":[{"text":": PropsWithChildren<CellProps...","position":{"line":36,"offset":21},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":36,"offset":22},"kind":"Type","whitespaceBefore":true},{"text":"date:","position":{"line":36,"offset":56},"kind":"Parameter","whitespaceAfter":true},{"text":": PropsWithChildren<CellProps...","position":{"line":49,"offset":21},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":49,"offset":22},"kind":"Type","whitespaceBefore":true},{"text":"value:","position":{"line":50,"offset":34},"kind":"Parameter","whitespaceAfter":true},{"text":": (queryStatus: QueryStatus) ...","position":{"line":63,"offset":36},"kind":"Type","whitespaceBefore":true},{"text":": Props","position":{"line":79,"offset":72},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":79,"offset":73},"kind":"Type","whitespaceBefore":true},{"text":"props:","position":{"line":80,"offset":74},"kind":"Parameter","whitespaceAfter":true},{"text":"date:","position":{"line":83,"offset":26},"kind":"Parameter","whitespaceAfter":true},{"text":"amount:","position":{"line":83,"offset":50},"kind":"Parameter","whitespaceAfter":true},{"text":"date:","position":{"line":83,"offset":37},"kind":"Parameter","whitespaceAfter":true},{"text":"date:","position":{"line":84,"offset":26},"kind":"Parameter","whitespaceAfter":true},{"text":"mutationFn:","position":{"line":89,"offset":60},"kind":"Parameter","whitespaceAfter":true},{"text":": AxiosPromise<RevPTransactio...","position":{"line":89,"offset":80},"kind":"Type","whitespaceBefore":true},{"text":": RevPTransaction[]","position":{"line":97,"offset":21},"kind":"Type","whitespaceBefore":true},{"text":"data:","position":{"line":99,"offset":62},"kind":"Parameter","whitespaceAfter":true},{"text":": { label: string; value: str...","position":{"line":101,"offset":20},"kind":"Type","whitespaceBefore":true},{"text":"factory:","position":{"line":102,"offset":5},"kind":"Parameter","whitespaceAfter":true},{"text":"deps:","position":{"line":103,"offset":5},"kind":"Parameter","whitespaceAfter":true},{"text":": { label: string; value: str...","position":{"line":102,"offset":7},"kind":"Type","whitespaceBefore":true},{"text":"callbackfn:","position":{"line":102,"offset":29},"kind":"Parameter","whitespaceAfter":true},{"text":": Card","position":{"line":102,"offset":37},"kind":"Type","whitespaceBefore":true},{"text":": string","position":{"line":106,"offset":18},"kind":"Type","whitespaceBefore":true},{"text":"name:","position":{"line":106,"offset":27},"kind":"Parameter","whitespaceAfter":true},{"text":": Date","position":{"line":107,"offset":17},"kind":"Type","whitespaceBefore":true},{"text":"name:","position":{"line":107,"offset":26},"kind":"Parameter","whitespaceAfter":true},{"text":": { field: ControllerRenderPr...","position":{"line":117,"offset":33},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":117,"offset":34},"kind":"Type","whitespaceBefore":true},{"text":": { field: ControllerRenderPr...","position":{"line":132,"offset":31},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":132,"offset":32},"kind":"Type","whitespaceBefore":true},{"text":": Date | null","position":{"line":137,"offset":32},"kind":"Type","whitespaceBefore":true},{"text":": Date","position":{"line":138,"offset":33},"kind":"Type","whitespaceBefore":true},{"text":"date:","position":{"line":138,"offset":47},"kind":"Parameter","whitespaceAfter":true},{"text":"...event:","position":{"line":140,"offset":34},"kind":"Parameter","whitespaceAfter":true},{"text":"name:","position":{"line":141,"offset":28},"kind":"Parameter","whitespaceAfter":true},{"text":"value:","position":{"line":141,"offset":38},"kind":"Parameter","whitespaceAfter":true},{"text":"date:","position":{"line":141,"offset":46},"kind":"Parameter","whitespaceAfter":true},{"text":"amount:","position":{"line":141,"offset":56},"kind":"Parameter","whitespaceAfter":true},{"text":": { field: ControllerRenderPr...","position":{"line":149,"offset":31},"kind":"Type","whitespaceBefore":true},{"text":": Element","position":{"line":149,"offset":32},"kind":"Type","whitespaceBefore":true},{"text":"date:","position":{"line":155,"offset":35},"kind":"Parameter","whitespaceAfter":true},{"text":"amount:","position":{"line":155,"offset":45},"kind":"Parameter","whitespaceAfter":true},{"text":": Date | null","position":{"line":158,"offset":32},"kind":"Type","whitespaceBefore":true},{"text":"...event:","position":{"line":159,"offset":34},"kind":"Parameter","whitespaceAfter":true},{"text":"date:","position":{"line":159,"offset":45},"kind":"Parameter","whitespaceAfter":true},{"text":"onValid:","position":{"line":168,"offset":35},"kind":"Parameter","whitespaceAfter":true},{"text":": { cardToken: string; dateFr...","position":{"line":168,"offset":45},"kind":"Type","whitespaceBefore":true},{"text":"variables:","position":{"line":168,"offset":55},"kind":"Parameter","whitespaceAfter":true},{"text":"queryStatus:","position":{"line":178,"offset":55},"kind":"Parameter","whitespaceAfter":true}]}
Info 2017 [19:51:19.350] request:
    {"seq":48,"type":"request","command":"getApplicableRefactors","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","startLine":126,"startOffset":17,"endLine":126,"endOffset":17}}
Perf 2018 [19:51:19.352] 48::getApplicableRefactors: elapsed time (in milliseconds) 2.1181
Info 2019 [19:51:19.352] response:
    {"seq":0,"type":"response","command":"getApplicableRefactors","request_seq":48,"success":true,"body":[{"name":"Convert export","description":"Convert default export to named export","actions":[{"name":"Convert default export to named export","description":"Convert default export to named export","kind":"refactor.rewrite.export.named","notApplicableReason":"Could not find export statement"},{"name":"Convert named export to default export","description":"Convert named export to default export","kind":"refactor.rewrite.export.default","notApplicableReason":"Could not find export statement"}]},{"name":"Convert import","description":"Convert namespace import to named imports","actions":[{"name":"Convert namespace import to named imports","description":"Convert namespace import to named imports","kind":"refactor.rewrite.import.named","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Convert import","description":"Convert named imports to namespace import","actions":[{"name":"Convert named imports to namespace import","description":"Convert named imports to namespace import","kind":"refactor.rewrite.import.namespace","notApplicableReason":"Selection is not an import declaration."}]},{"name":"Extract Symbol","description":"Extract function","actions":[{"name":"Extract Function","description":"Extract function","kind":"refactor.extract.function","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract Symbol","description":"Extract constant","actions":[{"name":"Extract Constant","description":"Extract constant","kind":"refactor.extract.constant","notApplicableReason":"Cannot extract empty range."}]},{"name":"Extract type","description":"Extract type","actions":[{"name":"Extract to typedef","description":"Extract to typedef","kind":"refactor.extract.typedef","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to type alias","description":"Extract to type alias","kind":"refactor.extract.type","notApplicableReason":"Selection is not a valid type node"},{"name":"Extract to interface","description":"Extract to interface","kind":"refactor.extract.interface","notApplicableReason":"Selection is not a valid type node"}]},{"name":"Generate 'get' and 'set' accessors","description":"Generate 'get' and 'set' accessors","actions":[{"name":"Generate 'get' and 'set' accessors","description":"Generate 'get' and 'set' accessors","kind":"refactor.rewrite.property.generateAccessors","notApplicableReason":"Could not find property for which to generate accessor"}]},{"name":"Move to a new file","description":"Move to a new file","actions":[{"name":"Move to a new file","description":"Move to a new file","kind":"refactor.move.newFile","notApplicableReason":"Selection is not a valid statement or statements"}]},{"name":"Convert to template string","description":"Convert to template string","actions":[{"name":"Convert to template string","description":"Convert to template string","kind":"refactor.rewrite.string","notApplicableReason":"Can only convert string concatenation"}]},{"name":"Infer function return type","description":"Infer function return type","actions":[{"name":"Infer function return type","description":"Infer function return type","kind":"refactor.rewrite.function.returnType","notApplicableReason":"Return type must be inferred from a function"}]}]}
Info 2020 [19:51:19.353] request:
    {"seq":50,"type":"request","command":"quickinfo","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","line":129,"offset":16}}
Perf 2021 [19:51:19.364] 50::quickinfo: elapsed time (in milliseconds) 11.1055
Info 2022 [19:51:19.364] response:
    {"seq":0,"type":"response","command":"quickinfo","request_seq":50,"success":true,"body":{"kind":"alias","kindModifiers":"declare","start":{"line":129,"offset":12},"end":{"line":129,"offset":22},"displayString":"(alias) const Controller: <TFieldValues extends FieldValues = FieldValues, TName extends Path<TFieldValues> = Path<TFieldValues>>(props: ControllerProps<TFieldValues, TName>) => import(\"react\").ReactElement<any, string | import(\"react\").JSXElementConstructor<any>>\nimport Controller","documentation":[],"tags":[]}}
Info 2023 [19:51:19.364] request:
    {"seq":51,"type":"request","command":"geterr","arguments":{"delay":0,"files":["/Users/aleksei.tsikov/git/project/src/Component.tsx"]}}
Perf 2024 [19:51:19.364] 51::geterr: async elapsed time (in milliseconds) 0.4231
Info 2025 [19:51:19.366] event:
    {"seq":0,"type":"event","event":"syntaxDiag","body":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","diagnostics":[]}}
Info 2026 [19:51:19.825] event:
    {"seq":0,"type":"event","event":"requestCompleted","body":{"request_seq":51}}
Info 2027 [19:51:19.827] request:
    {"seq":52,"type":"request","command":"quickinfo","arguments":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","line":129,"offset":16}}
Perf 2028 [19:51:19.839] 52::quickinfo: elapsed time (in milliseconds) 11.8457
Info 2029 [19:51:19.839] response:
    {"seq":0,"type":"response","command":"quickinfo","request_seq":52,"success":true,"body":{"kind":"alias","kindModifiers":"declare","start":{"line":129,"offset":12},"end":{"line":129,"offset":22},"displayString":"(alias) const Controller: <TFieldValues extends FieldValues = FieldValues, TName extends Path<TFieldValues> = Path<TFieldValues>>(props: ControllerProps<TFieldValues, TName>) => import(\"react\").ReactElement<any, string | import(\"react\").JSXElementConstructor<any>>\nimport Controller","documentation":[],"tags":[]}}
Info 2030 [19:51:19.839] request:
    {"seq":53,"type":"request","command":"geterr","arguments":{"delay":0,"files":["/Users/aleksei.tsikov/git/project/src/Component.tsx"]}}
Perf 2031 [19:51:19.840] 53::geterr: async elapsed time (in milliseconds) 0.1562
Info 2032 [19:51:19.841] event:
    {"seq":0,"type":"event","event":"syntaxDiag","body":{"file":"/Users/aleksei.tsikov/git/project/src/Component.tsx","diagnostics":[]}}

...

Here's the minimal repro (basically, only react and react-hook-form) https://github.com/atsikov/ts-4.5.2-perf-issue

I've found out that this issue is reproducible with react-hook-form < 7.17.2. In react-hook-form/react-hook-form@85097ae types were improved, so with >= 7.17.2 type hints and autocomplete work as expected.

...

4.5.2 created 30 times more (intermediate?) types and consumed 8 times more memory.

TS 4.4.4 extended diagnostics
Files:                         50
Lines of Library:           26160
Lines of Definitions:       24762
Lines of TypeScript:           14
Lines of JavaScript:            0
Lines of JSON:                  0
Lines of Other:                 0
Nodes of Library:          112958
Nodes of Definitions:       60457
Nodes of TypeScript:           55
Nodes of JavaScript:            0
Nodes of JSON:                  0
Nodes of Other:                 0
Identifiers:                58865
Symbols:                    39464
Types:                      28967
Instantiations:             86282
Memory used:               93470K
Assignability cache size:   17107
Identity cache size:           62
Subtype cache size:             0
Strict subtype cache size:      0
I/O Read time:              0.01s
Parse time:                 0.48s
ResolveModule time:         0.02s
ResolveTypeReference time:  0.00s
Program time:               0.53s
Bind time:                  0.21s
Check time:                 0.64s
printTime time:             0.00s
Emit time:                  0.00s
Total time:                 1.39s
TS 4.5.2 extended diagnostics
Files:                          50
Lines of Library:            25009
Lines of Definitions:        24762
Lines of TypeScript:            14
Lines of JavaScript:             0
Lines of JSON:                   0
Lines of Other:                  0
Nodes of Library:           112478
Nodes of Definitions:        60453
Nodes of TypeScript:            55
Nodes of JavaScript:             0
Nodes of JSON:                   0
Nodes of Other:                  0
Identifiers:                 58582
Symbols:                     39008
Types:                     1048793
Instantiations:             135356
Memory used:               724704K
Assignability cache size:  7007111
Identity cache size:            27
Subtype cache size:              0
Strict subtype cache size:       0
I/O Read time:               0.03s
Parse time:                  0.49s
ResolveModule time:          0.02s
ResolveTypeReference time:   0.00s
Program time:                0.57s
Bind time:                   0.22s
Check time:                 93.30s
printTime time:              0.00s
Emit time:                   0.00s
Total time:                 94.09s

Originally posted by @atsikov in #46735 (comment)

Metadata

Metadata

Assignees

Labels

Needs InvestigationThis issue needs a team member to investigate its status.RescheduledThis issue was previously scheduled to an earlier milestone

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions