Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New error: Type instantiation is excessively deep and possibly infinite. #37613

Open
amcasey opened this issue Mar 25, 2020 · 10 comments
Open

New error: Type instantiation is excessively deep and possibly infinite. #37613

amcasey opened this issue Mar 25, 2020 · 10 comments
Labels
Needs Investigation This issue needs a team member to investigate its status.
Milestone

Comments

@amcasey
Copy link
Member

amcasey commented Mar 25, 2020

This one's actually new in 3.8. There are some additional errors in 3.9 - I'll post them below.

https://github.com/davidkpiano/xstate/blob/2e0bd5dcb11c8ffc5bfc9fba1d3c66fb7546c4bd/packages/core/src/stateUtils.ts#L157

packages/core/src/stateUtils.ts:157:36 - error TS2589: Type instantiation is excessively deep and possibly infinite.

157   return getValueFromAdj(rootNode, getAdjList(config));
                                       ~~~~~~~~~~~~~~~~~~

packages/core/src/State.ts:181:12 - error TS2589: Type instantiation is excessively deep and possibly infinite.

181     return new State(config);
               ~~~~~~~~~~~~~~~~~

packages/core/src/interpreter.ts:208:5 - error TS2589: Type instantiation is excessively deep and possibly infinite.

208     this.parent = parent;
        ~~~~~~~~~~~~~~~~~~~~

packages/core/src/interpreter.ts:783:13 - error TS2345: Argument of type '{ type: string; data: any; }' is not assignable to parameter of type 'string | EventObject | Event<EventObject>[] | Event<EventObject>'.
  Object literal may only specify known properties, and 'data' does not exist in type 'EventObject'.

783             data: err
                ~~~~~~~~~

To repro:

  1. yarn
  2. tsc -b -f tsconfig.monorepo.json
@amcasey amcasey added this to the TypeScript 3.9.1 milestone Mar 25, 2020
@amcasey
Copy link
Member Author

amcasey commented Mar 25, 2020

3.9 errors:

packages/core/src/stateUtils.ts:157:36 - error TS2589: Type instantiation is excessively deep and possibly infinite.

157   return getValueFromAdj(rootNode, getAdjList(config));
                                       ~~~~~~~~~~~~~~~~~~

packages/core/src/actions.ts:108:9 - error TS2783: 'type' is specified more than once, so this usage will be overwritten.

108         type,
            ~~~~

  packages/core/src/actions.ts:109:9
    109         ...exec,
                ~~~~~~~
    This spread always overwrites this property.

packages/core/src/State.ts:181:12 - error TS2589: Type instantiation is excessively deep and possibly infinite.

181     return new State(config);
               ~~~~~~~~~~~~~~~~~

packages/core/src/interpreter.ts:208:5 - error TS2589: Type instantiation is excessively deep and possibly infinite.

208     this.parent = parent;
        ~~~~~~~~~~~~~~~~~~~~

packages/core/src/interpreter.ts:783:13 - error TS2345: Argument of type '{ type: string; data: any; }' is not assignable to parameter of type 'string | EventObject | Event<EventObject>[] | Event<EventObject>'.
  Object literal may only specify known properties, and 'data' does not exist in type 'EventObject'.

783             data: err
                ~~~~~~~~~

packages/core/src/StateNode.ts:285:5 - error TS2322: Type 'MachineOptions<TContext, any> & Partial<MachineOptions<TContext, TEvent>>' is not assignable to type 'MachineOptions<TContext, TEvent>'.
  Types of property '_parent' are incompatible.
    Type '(StateNode<TContext, any, any, any> & StateNode<TContext, any, TEvent, any>) | undefined' is not assignable to type 'StateNode<TContext, any, TEvent, any> | undefined'.
      Type 'StateNode<TContext, any, any, any> & StateNode<TContext, any, TEvent, any>' is not assignable to type 'StateNode<TContext, any, TEvent, any> | undefined'.
        Type 'StateNode<TContext, any, any, any> & StateNode<TContext, any, TEvent, any>' is not assignable to type 'StateNode<TContext, any, TEvent, any>'.
          Types of property 'parent' are incompatible.
            Type '(StateNode<TContext, any, any, any> & StateNode<TContext, any, TEvent, any>) | undefined' is not assignable to type 'StateNode<TContext, any, TEvent, any> | undefined'.
              Type 'StateNode<TContext, any, any, any> & StateNode<TContext, any, TEvent, any>' is not assignable to type 'StateNode<TContext, any, TEvent, any> | undefined'.

285     this.options = Object.assign(createDefaultOptions<TContext>(), options);
        ~~~~~~~~~~~~

packages/core/src/StateNode.ts:1824:7 - error TS2322: Type 'TransitionsConfigArray<TContext, TEvent>' is not assignable to type '(TransitionConfig<TContext, EventObject> & { event: string; })[]'.
  Type '(TransitionConfig<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject> & { ...; }) | (TransitionConfig<...> & { ...; }) | (TransitionConfig<...> & { ...; })' is not assignable to type 'TransitionConfig<TContext, EventObject> & { event: string; }'.
    Type 'TransitionConfig<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject> & { ...; }' is not assignable to type 'TransitionConfig<TContext, EventObject> & { event: string; }'.
      Type 'TransitionConfig<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject> & { ...; }' is not assignable to type 'TransitionConfig<TContext, EventObject>'.
        Types of property 'target' are incompatible.
          Type 'string | StateNode<TContext, any, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject, any> | (string | StateNode<...>)[] | undefined' is not assignable to type 'string | StateNode<TContext, any, EventObject, any> | (string | StateNode<TContext, any, EventObject, any>)[] | undefined'.
            Type 'StateNode<TContext, any, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject, any>' is not assignable to type 'string | StateNode<TContext, any, EventObject, any> | (string | StateNode<TContext, any, EventObject, any>)[] | undefined'.
              Type 'StateNode<TContext, any, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject, any>' is not assignable to type 'StateNode<TContext, any, EventObject, any>'.
                The types of 'config.on' are incompatible between these types.
                  Type 'TransitionsConfigMap<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject> | TransitionsConfigArray<...> | undefined' is not assignable to type 'TransitionsConfigMap<TContext, EventObject> | TransitionsConfigArray<TContext, EventObject> | undefined'.
                    Type 'TransitionsConfigMap<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject>' is not assignable to type 'TransitionsConfigMap<TContext, EventObject> | TransitionsConfigArray<TContext, EventObject> | undefined'.
                      Type 'TransitionsConfigMap<TContext, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject>' is not assignable to type 'TransitionsConfigMap<TContext, EventObject>'.
                        Type 'SingleOrArray<string | StateNode<TContext, any, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject, any> | (TransitionConfig<...> & { ...; }) | undefined>' is not assignable to type 'SingleOrArray<string | StateNode<TContext, any, EventObject, any> | (TransitionConfig<TContext, EventObject> & { ...; }) | undefined>'.
                          Type 'StateNode<TContext, any, ActionTypes.NullEvent extends TEvent["type"] ? Extract<TEvent, { type: TEvent["type"] & ActionTypes.NullEvent; }> : EventObject, any>' is not assignable to type 'SingleOrArray<string | StateNode<TContext, any, EventObject, any> | (TransitionConfig<TContext, EventObject> & { ...; }) | undefined>'.

1824       onConfig = this.config.on;
           ~~~~~~~~

packages/core/src/StateNode.ts:1824:7 - error TS2589: Type instantiation is excessively deep and possibly infinite.

1824       onConfig = this.config.on;
           ~~~~~~~~~~~~~~~~~~~~~~~~~

@ahejlsberg
Copy link
Member

I took a quick look. The excessively depth errors are caused by these two types in src\types.ts:

type TransitionsConfigMap<TContext, TEvent extends EventObject> = {
  [K in TEvent['type'] | NullEvent['type'] | '*']?: SingleOrArray<
    | TransitionConfigTargetShortcut<TContext, TEvent>
    | (TransitionConfig<
        TContext,
        K extends TEvent['type'] ? Extract<TEvent, { type: K }> : EventObject
      > & {
        event?: undefined;
      })
  >;
};

type TransitionsConfigArray<TContext, TEvent extends EventObject> = Array<
  {
    [K in TEvent['type'] | NullEvent['type'] | '*']: TransitionConfig<
      TContext,
      K extends TEvent['type'] ? Extract<TEvent, { type: K }> : EventObject
    > & {
      event: K;
    };
  }[TEvent['type'] | NullEvent['type'] | '*']
>;

The problem appears to be exploration of the conditional type constrains that keep spiraling until we hit the depth limiter. When I change K extends TEvent['type'] ? Extract<TEvent, { type: K }> : EventObject to just EventObject in each of the types, the check time drops by 50% and the type count drops by 60-70%. I'm not exactly sure what these types are doing, but simplifying them definitely has a major effect.

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label Mar 27, 2020
@DanielRosenwasser
Copy link
Member

FYI @davidkpiano

@davidkpiano
Copy link

Working on a fix for this, but I found some odd type behavior:

type ResolvedEvent = ({
    type: "CHANGE";
    value: string;
} & {
    type: "CHANGE";
}) | ({
    type: "SAVE";
} & {
    type: "CHANGE";
})

function assign(event: ResolvedEvent) {
    return event.value;
}

This will produce the following error for event.value:

Property 'value' does not exist on type 'ResolvedEvent'.
  Property 'value' does not exist on type '{ type: "SAVE"; } & { type: "CHANGE"; }'.(2339)

I would expect ResolvedEvent to be narrowed to { type: 'CHANGE'; value: string } since { type: 'SAVE' } & { type: 'CHANGE' } should be never.

@ahejlsberg
Copy link
Member

@davidkpiano
Copy link

Ah, no, I was using 3.8.3. Glad to see it's fixed - was that specific issue tracked?

@ahejlsberg
Copy link
Member

Glad to see it's fixed - was that specific issue tracked?

Yes, it was fixed by #36696.

@vasilii-kovalev
Copy link

vasilii-kovalev commented Jul 9, 2021

I'm facing the error in such a case:

type GetLetters<Text> = Text extends `${infer Letter}${infer Rest}` ? Letter | GetLetters<Rest> : never;
type Letters = GetLetters<"some very, very long text">;

The error appears when the text is longer than or equal to 24 characters.

Playground.

@ahejlsberg
Copy link
Member

ahejlsberg commented Jul 13, 2021

@vasilii-kovalev We currently have a limit of 50 nested type instantiations, which in your case equates to 24 characters because each level consists of a conditional type and a union type. A good workaround is to "batch" for some number of characters. For example, this processes characters in batches of 10:

type GetLetters<Text> =
    Text extends `${infer C0}${infer C1}${infer C2}${infer C3}${infer C4}${infer C5}${infer C6}${infer C7}${infer C8}${infer C9}${infer Rest}` ? C0 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | GetLetters<Rest> :
    Text extends `${infer C}${infer Rest}` ? C | GetLetters<Rest> : never;

This should allow you to process strings of up to 250 characters.

Our currently limit of 50 is probably a bit low now that we have template literal strings and we're looking at increasing it. However, there will always be some limit to how deep you can nest recursive types given that Node.js defaults the call stack size to 1Mb or less.

@vasilii-kovalev
Copy link

vasilii-kovalev commented Jul 14, 2021

@ahejlsberg, thank you for the explanation!

I have a library, which depends on the approach I described above.
Some usage examples:

// "Hello, John!"
console.log(
  hydrateText(
    "Hello, {username}!",
    // No errors
    { username: "John" },
    {
      prefix: "{",
      suffix: "}",
    },
  ),
);

console.log(
  hydrateText(
    "Hello, {username}!",
    // Error: `username` is missing
    {},
    {
      prefix: "{",
      suffix: "}",
    },
  ),
);

// "{U}<S>(E)[R]"
console.log(
  hydrateText(
    "user",
    {
      u: "{U}",
      s: "<S>",
      e: "(E)",
      r: "[R]",
    },
    {
      prefix: "",
      suffix: "",
    },
  ),
);

I've created a playground with types only, that shows how the limitation affects the library's behavior.

I don't think that the "batching" can help a lot in this case, because in order to "batch" even two variables, it would be necessary to significantly complicate the GetVariables type (which is already complex enough). And it will only increase the number of variables from 8 to ~16, I suppose. For now, I recommend breaking the string into several pieces.

P.S.: I've noticed some strange behavior with the error. In the playground above, it is hidden until MultipleVariablesWithoutStreet is commented out. Also, if there are more than one of this error, the 2nd, 3d, etc. are not marked as having the error, even if the first one has @ts-ignore or @ts-expect-error (another playground).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests

6 participants