Repository navigation
New error: Excessive stack depth comparing types #37600
Copy link
Copy link
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue
Milestone
Description
So many things went wrong that I'm just going to paste the list of errors here.
One location for context: https://github.com/react-page/react-page/blob/fb17a032bd6415080e468c0a43fd0f9c45a22cbe/packages/core/src/service/plugin/default.tsx#L51
error TS2321: Excessive stack depth comparing types '{ Component: SFC<ContentPluginProps<{ value: string; }>>; name: string; version: string; createInitialState: () => { value: string; }; }' and 'Pick<ContentPluginProps<{}>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | ... 9 more ... | "createInitialState">'.
packages/core/src/service/plugin/default.tsx:51:3 - error TS2322: Type 'SFC<ContentPluginProps<{ value: string; }>>' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{}, ContentPluginExtraProps<{}>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'FunctionComponent<ContentPluginProps<{ value: string; }>>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any> & FunctionComponent<PluginProps<{}, ContentPluginExtraProps<{}>> & ContentPluginExtraProps<...>>'.
Type 'FunctionComponent<ContentPluginProps<{ value: string; }>>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any>'.
The types of 'propTypes.Component' are incompatible between these types.
Type 'Validator<(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{ value: string; }, ContentPluginExtraProps<{ value: string; }>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & Fun...' is not assignable to type 'Validator<(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)>'.
Type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{ value: string; }, ContentPluginExtraProps<{ value: string; }>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionCompo...' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{ value: string; }, ContentPluginExtraProps<{ value: string; }>> & ContentPluginExtraProps<...>, any>' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{ value: string; }, ContentPluginExtraProps<{ value: string; }>> & ContentPluginExtraProps<...>, any>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<{ value: string; }, ContentPluginExtraProps<{ value: string; }>> & ContentPluginExtraProps<...>, any>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any>'.
The types of 'propTypes.Component[nominalTypeHack].type' are incompatible between these types.
Type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any> & ComponentClass<...> & FunctionComponent<...>) | ... 14 more ... | (FunctionComponent<...> & ... 2 more ... & ComponentClass<...>)' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any> & ComponentClass<...> & FunctionComponent<...>' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any> & ComponentClass<...> & FunctionComponent<...>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any> & FunctionComponent<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>>'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any> & ComponentClass<...> & FunctionComponent<...>' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any>'.
51 Component: Default,
~~~~~~~~~
packages/core/src/service/plugin/classes.ts:112:3
112 Component?: PluginComponentType<ContentPluginProps<T>>;
~~~~~~~~~
The expected type comes from property 'Component' which is declared here on type 'Pick<ContentPluginProps<{}>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | ... 9 more ... | "createInitialState">'
error TS2321: Excessive stack depth comparing types '{ Component: (props: ContentPluginProps<{}>) => Element; name: string; version: string; }' and 'Pick<ContentPluginProps<any>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | ... 9 more ... | "createInitialState">'.
packages/core/src/service/plugin/missing.tsx:51:3 - error TS2322: Type '(props: ContentPluginProps<{}>) => JSX.Element' is not assignable to type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type '(props: ContentPluginProps<{}>) => JSX.Element' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any> & FunctionComponent<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>>'.
Type '(props: ContentPluginProps<{}>) => JSX.Element' is not assignable to type 'ComponentClass<ContentPluginProps<any>, any>'.
Type '(props: ContentPluginProps<{}>) => Element' provides no match for the signature 'new (props: ContentPluginProps<any>, context?: any): Component<ContentPluginProps<any>, any, any>'.
51 Component: ContentMissingComponent,
~~~~~~~~~
packages/core/src/service/plugin/classes.ts:112:3
112 Component?: PluginComponentType<ContentPluginProps<T>>;
~~~~~~~~~
The expected type comes from property 'Component' which is declared here on type 'Pick<ContentPluginProps<any>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | ... 9 more ... | "createInitialState">'
packages/core/src/reducer/editable/index.ts:53:17 - error TS2322: Type '{ layout: { plugin: any; state: any; }; id: any; rows: { id: string; cells: { [key: string]: any; id: string; content?: { plugin: ContentPlugin<any>; state?: object; }; layout?: { plugin: LayoutPlugin<any>; state?: object; }; size?: number; }[]; }[]; ... 10 more ...; levels?: Levels; }' is not assignable to type 'AbstractCell<Row>'.
Types of property 'rows' are incompatible.
Type '{ id: string; cells: { [key: string]: any; id: string; content?: { plugin: ContentPlugin<any>; state?: object; }; layout?: { plugin: LayoutPlugin<any>; state?: object; }; size?: number; }[]; }[]' is not assignable to type 'Row[]'.
Type '{ id: string; cells: { [key: string]: any; id: string; content?: { plugin: ContentPlugin<any>; state?: object; }; layout?: { plugin: LayoutPlugin<any>; state?: object; }; size?: number; }[]; }' is not assignable to type 'Row'.
Types of property 'cells' are incompatible.
Type '{ [key: string]: any; id: string; content?: { plugin: ContentPlugin<any>; state?: object; }; layout?: { plugin: LayoutPlugin<any>; state?: object; }; size?: number; }[]' is not assignable to type 'AbstractCell<Row>[]'.
Type '{ [key: string]: any; id: string; content?: { plugin: ContentPlugin<any>; state?: object; }; layout?: { plugin: LayoutPlugin<any>; state?: object; }; size?: number; }' is not assignable to type 'AbstractCell<Row>'.
The types of 'layout.plugin.handleFocus' are incompatible between these types.
Type '(props: ContentPluginProps<any>, focusSource: string, ref: HTMLElement) => void' is not assignable to type '(props: PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<any>, focusSource: string, ref: HTMLElement) => void'.
Types of parameters 'props' and 'props' are incompatible.
Type 'PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<any>' is not assignable to type 'ContentPluginProps<any>'.
Type 'PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<any>' is missing the following properties from type 'ContentPluginExtraProps<any>': isEditMode, isResizeMode, isInsertMode, isPreviewMode, isLayoutMode
53 const c: Cell = {
~
packages/core/src/components/HotKey/Decorator.tsx:194:50 - error TS2615: Type of property 'editable' circularly references itself in mapped type '{ isEditMode: Selector<unknown, boolean>; focus: Selector<unknown, string>; node: Selector<unknown, (id: string, _editable: string) => Row | AbstractCell<Row>>; searchNodeEverywhere: Selector<...>; editable: Selector<...>; editables: Selector<...>; }'.
194 const mapStateToProps = createStructuredSelector({
~
195 isEditMode,
~~~~~~~~~~~~~
...
204 editables,
~~~~~~~~~~~~
205 });
~
packages/core/src/components/Row/index.tsx:65:50 - error TS2615: Type of property 'rawNode' circularly references itself in mapped type '{ isLayoutMode: Selector<unknown, boolean>; config: Selector<unknown, Config>; isResizeMode: Selector<unknown, boolean>; isInsertMode: Selector<unknown, boolean>; isEditMode: Selector<...>; node: Selector<...>; rawNode: Selector<...>; }'.
65 const mapStateToProps = createStructuredSelector({
~
66 isLayoutMode,
~~~~~~~~~~~~~~~
...
73 node(state, props),
~~~~~~~~~~~~~~~~~~~~~~~
74 });
~
packages/core/src/components/Cell/index.tsx:121:50 - error TS2615: Type of property 'rawNode' circularly references itself in mapped type '{ isPreviewMode: Selector<unknown, boolean>; isEditMode: Selector<unknown, boolean>; isResizeMode: Selector<unknown, boolean>; isInsertMode: Selector<unknown, boolean>; isLayoutMode: Selector<...>; config: Selector<...>; node: Selector<...>; rawNode: Selector<...>; }'.
121 const mapStateToProps = createStructuredSelector({
~
122 isPreviewMode,
~~~~~~~~~~~~~~~~
...
130 rawNode: (state: RootState, props: NodeProps) => () => node(state, props),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
131 });
~
packages/core/src/components/Editable/Inner/index.tsx:105:56 - error TS2345: Argument of type 'Pick<ContentPluginProps<any>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | ... 10 more ... | "createInitialState"> | Pick<...>' is not assignable to parameter of type 'Pick<LayoutPluginProps<any>, "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | "unserialize" | "description" | "handleRemoveHotKey" | ... 7 more ... | "createInitialChildren">'.
Type 'Pick<ContentPluginProps<any>, "allowInlineNeighbours" | "isInlineable" | "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | ... 9 more ... | "createInitialState">' is not assignable to type 'Pick<LayoutPluginProps<any>, "Component" | "name" | "version" | "IconComponent" | "hideInMenu" | "text" | "serialize" | "unserialize" | "description" | "handleRemoveHotKey" | ... 7 more ... | "createInitialChildren">'.
Types of property 'Component' are incompatible.
Type '(ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)' is not assignable to type '(ComponentClass<LayoutPluginProps<any>, any> & ComponentClass<PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>' is not assignable to type '(ComponentClass<LayoutPluginProps<any>, any> & ComponentClass<PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<...>, any>) | (ComponentClass<...> & FunctionComponent<...>) | (FunctionComponent<...> & ComponentClass<...>) | (FunctionComponent<...> & FunctionComponent<...>)'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>' is not assignable to type 'ComponentClass<LayoutPluginProps<any>, any> & ComponentClass<PluginProps<any, LayoutPluginExtraProps<any>> & LayoutPluginExtraProps<...>, any>'.
Type 'ComponentClass<ContentPluginProps<any>, any> & ComponentClass<PluginProps<any, ContentPluginExtraProps<any>> & ContentPluginExtraProps<...>, any>' is not assignable to type 'ComponentClass<LayoutPluginProps<any>, any>'.
105 this.props.createFallbackCell(new LayoutPlugin(defaultPlugin), id);
~~~~~~~~~~~~~
To repro:
yarntsc -b -f packages
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue
Activity
weswigham commentedon Apr 8, 2020
OK, so, two seperate things going on here, probably. The first one, from the title: the change that introduced this definitely seems to be #37195. The structural comparison of the source intersection to the target is definitely what triggers the new errors. Now, what's going on is we have
both
ContentPluginExtraPropsandPluginPropshave aComponentproperty, and they have differing typesand
The comparison stack between the expression type and that intersection is maybe too long to reasonably explain thoroughly, but suffice to say that we end up pingponging around between the proprieties on a class/function component which refer to props (
propTypes,defaultProps) and the props themselves which refer to a component (theComponentfield of the provided props), and we don't notice that we're essentially structurally comparing a deep type with self-augmenting back references (the back references are never identical because of the type intersected with it which is essentially a noop intersection member based on the current comparison cursor position in the type structure). Ultimately, we comparepropTypes.Component[nominalTypeHack].type.propTypes....(yes, it keeps looping for a bit) and the types we encounter as never quite the same, as on each iteration the definition ofComponentinPluginPropsessentially intersects the current position's type into the next position, growing the next result's intersection. Do note that this does bottom out (in reasonable time, no less) - if the maximum depth inrecursiveTypeRelatedTois raised from 100 to 150ish. That's because the exploration continues only for every combination of{}andanydefault arguments multiplied by every combination ofClassComponentandFunctionComponent, plus every combination ofContentPluginExtraPropsandPluginProps, after which point all the possible combinations have been made, tested, and shown to be sufficiently structurally similar.There is a trivial fix/workaround/improvement which can be applied to
react-pageitself:actually providing
ContentPluginExtraProps's type parameter makes the props sufficiently similar that we actual find success quickly (as we're no longer comparingContentPluginExtraProps<any>toContentPluginExtraProps<{}>nearly as much), and avoids the stack depth limit.Anders Hejlsberg (@ahejlsberg) because this is resolvable with an increase to maximal
depth, we're not fundamentally doing anything we don't intend to, indeed, it simply seems like the conflicting property check, when applied on a self-referential structure, is simply extraordinarily costly to perform, as it crawls the entire structural matrix reachable from the inputs, which, in this case, is the powerset of a few inputs, resulting a a large number of recursive calls. You might want to try a perf analysis ofreact-pagefrom before/after #37195, just in case - thematerial-uiperf suite might not contain any self referential structures which trigger this behavior, but, beyond that, it might make sense for the conflicting property check to be disabled if we're already doing a conflicting property check (I know excess checks are deep, but we know excess checks on object literals never contain self references!)Now, the other issue, the circular reference in mapped type thing, I have yet to look at. I'll look into it next.
weswigham commentedon Apr 8, 2020
The circular mapped type properties are new (the error itself is new, the behavior leading to the circularity used to simply cache an
any, rather than report an error), however the circular behavior leading to the error here is maybe undesirable - insidegetReturnTypeFromBodywe're looking for a contextual signature even in cases where we don't strictly need it (the function isn't a generator, so doesn't have a contextualnextType, doesn't have a literal-valued return so won't need literal widening, and , and doesn't have a return type affected by widening, so we don't need to decide if we should emit widening errors). Ultimately the circularity is triggered because the parameter is a mapped type, making the contextual type at an object member of that argument position an indexed access on a reverse mapped type, which in turn, causes a resolution of... the same property in the same mapped type. Essentially the function contextually types itself under our current rules, strange as that sounds... here's a minimal repro:the circular reference error goes away if the arity perfectly matches, however in the real world example scenario, this is just one of two overloads for
creatureStructuredSelector, the second of which definitely allows the extra parameter; so it throwing a circularity error because of an (unused) contextual type when checking the first overload is definitely undesirable.