“Type instantiation is excessively deep and possibly infinite” but only in a large codebase
See original GitHub issueTypeScript Version: 3.7.2, 3.8.0-dev.20191102 (worked in 3.6)
Search Terms:
- Type instantiation is excessively deep and possibly infinite.ts(2589)
- Mapped types
- Generics
- Conditional types
Code
Note: this issue manifests itself only in our codebase. When you run the same code in TypeScript Playground, it seems to be working fine.
The snippet is hardly minimal, but I reduced it as much as I could. I recorded a video where exactly the same code yields an error different than the one in TypeScript Playground. I tried with two versions of TypeScript: 3.7.2
and 3.8.0-dev.20191102
. It worked correctly with 3.6
.
Since @sheetalkamat and @DanielRosenwasser have access to our repository, you’re welcome to have a look at this PR. Copy-paste the code below anywhere in the project to see the error.
The versions of types used:
@types/history@4.7.3
@types/react@16.9.11
@types/react-router-dom@5.1.0
@types/recompose@0.30.7
Note: Interestingly enough, if you change:
- declare const Button: React.FunctionComponent<Omit<Props, never>>;
+ declare const Button: React.FunctionComponent<Props>;
it works again despite the fact Omit<Props, never>
should be the same as just Props
.
Source code
import { History } from 'history'; // "4.7.3"
import * as React from 'react'; // "16.9.11"
import { LinkProps, RouteComponentProps, withRouter } from 'react-router-dom'; // "5.1.0"
import { getDisplayName } from 'recompose'; // "0.30.7"
declare function isDefined<T>(candidate: T | null | undefined): candidate is T;
declare function isString(value?: any): value is string;
type ObjectOmit<T extends K, K> = Omit<T, keyof K>;
type OnClick = NonNullable<React.ComponentProps<'button'>['onClick']>;
type OnClickProp = {
/** If there is a custom click handler, we must preserve it. */
onClick?: OnClick;
};
type ProvidedProps = OnClickProp;
type InputProps = OnClickProp & {
/** Note: we want this helper to work with all sorts of modals, not just those backed by query
* parameters (e.g. `/photo/:id/info`), which is why this must accept a full location instead of a
* `Modal` type.
* */
to: Exclude<LinkProps['to'], Function>;
};
const buildClickHandler = ({
to,
onClick,
history,
}: InputProps & {
history: History;
}): OnClick => {
const navigate = () => {
// https://github.com/Microsoft/TypeScript/issues/14107
isString(to) ? history.push(to) : history.push(to);
};
return event => {
[onClick, navigate].filter(isDefined).forEach(callback => callback(event));
};
};
/** See the test for an example of usage. */
export const enhance = <ComposedProps extends ProvidedProps>(
ComposedComponent: React.ComponentType<ComposedProps>,
) => {
type PassThroughComposedProps = ObjectOmit<ComposedProps, ProvidedProps>;
type OwnProps = InputProps & RouteComponentProps<never> & PassThroughComposedProps;
type Props = OwnProps;
const displayName = `CreateModalLink(${getDisplayName(ComposedComponent)})`;
const ModalLink: React.FunctionComponent<Props> = ({
to,
onClick,
history,
// We specify these just to omit them from rest props below
location,
match,
staticContext,
...passThroughComposedProps
}) => {
const clickHandler = buildClickHandler({ to, onClick, history });
const composedProps: ComposedProps = {
// Note: this is technically unsafe, since the composed component may have props
// with names matching the ones we're omitting.
// https://github.com/microsoft/TypeScript/issues/28884#issuecomment-503540848
...((passThroughComposedProps as unknown) as PassThroughComposedProps),
onClick: clickHandler,
} as ComposedProps;
return <ComposedComponent {...composedProps} />;
};
ModalLink.displayName = displayName;
return withRouter(ModalLink);
};
type Props = React.ComponentPropsWithoutRef<'button'> &
Required<Pick<React.ComponentPropsWithoutRef<'button'>, 'type'>>;
/**
* This one errors.
*/
declare const Button: React.FunctionComponent<Omit<Props, never>>;
/**
* This one works.
*/
// declare const Button: React.FunctionComponent<Props>;
const EnhancedButton = enhance(Button);
/**
* Type instantiation is excessively deep and possibly infinite.ts(2589).
*/
() => <EnhancedButton></EnhancedButton>;
Expected behavior:
I should get a proper error about missing properties (not the one about type instantiation):
Type '{}' is missing the following properties from type 'Readonly<Pick<OwnProps, "form" | "style" | "title" | "onClick" | "to" | "key" | "autoFocus" | "disabled" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | ... 252 more ... | "onTransitionEndCapture">>': to, type(2739)
Actual behavior:
I’m getting this:
Type instantiation is excessively deep and possibly infinite.ts(2589).
Playground Link:
Related Issues:
Issue Analytics
- State:
- Created 4 years ago
- Reactions:92
- Comments:55 (8 by maintainers)
Top GitHub Comments
Guys, please show your interest in #44997 (thumb up!) to resolve the issue faster
Currently, the limitation thresholds are hard-coded, to
50
and5000000
.src/compiler/checker.ts
To make this configurable, I pushed a PR #44997, which makes those values be able to be set by
compilerOptions
that can be passed through CLI options ortsconfig.json
, like an example below.Though the PR is incomplete, just showing your interest would make its priority higher.
Thanks.
I ran into this with xstate. The types work with 3.7.5 but upgrading to 3.8.2 gives
error TS2589: Type instantiation is excessively deep and possibly infinite.
I made a quick gist to reproduce the issue, hope it helps: https://gist.github.com/qtiki/5f97233f609e516b5439e926514e85d9
Download the gist and run
yarn
andyarn tsc
to get the error (or somenpm
commands if you prefer).