question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

“Type instantiation is excessively deep and possibly infinite” but only in a large codebase

See original GitHub issue

TypeScript 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:

Playground Link

Related Issues:

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:92
  • Comments:55 (8 by maintainers)

github_iconTop GitHub Comments

88reactions
jjangga0214commented, Jul 13, 2021

Guys, please show your interest in #44997 (thumb up!) to resolve the issue faster

Currently, the limitation thresholds are hard-coded, to 50 and 5000000.

src/compiler/checker.ts

if (
  instantiationDepth === 50 ||
  instantiationCount >= 5000000
) {
  // We have reached 50 recursive type instantiations and there is a very high likelyhood we're dealing
  // with a combination of infinite generic types that perpetually generate new type identities. We stop
  // the recursion here by yielding the error type.
  error(
    currentNode,
    Diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite
  );
  return errorType;
}

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 or tsconfig.json, like an example below.

{
  "compilerOptions": {
    "instantiationDepthLimit": 100,
    "instantiationCountLimit": 10000000
  }
}

Though the PR is incomplete, just showing your interest would make its priority higher.

Thanks.

14reactions
qtikicommented, Feb 25, 2020

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 and yarn tsc to get the error (or some npm commands if you prefer).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using recursive type alias in generic results in error
7.5 throws Type instantiation is excessively deep and possibly infinite.(2589) when I'm trying to use recursive type alias in generic function.
Read more >
I wrote a type for enforcing some type safety on 'path ...
Hey again, I actually already commented below, but I just tried ... getting the Type instantiation is excessively deep and possibly infinite ......
Read more >
Documentation - TypeScript 4.5
error: Type instantiation is excessively deep and possibly infinite. type Test = Unpack<InfiniteBox<number>>;. The above example is intentionally simple and ...
Read more >
৪ মার্চ, ২০২০
An unintuitive error (“Type instantiation is excessively deep and possibly infinite”). Fixed by casting the type (IMessage) to the generic “Bubble”.
Read more >
Announcing TypeScript 4.5
This change was made for TypeScript 4.5, but was also back-ported to ... Type instantiation is excessively deep and possibly infinite. type ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found