Skip to content

Maximum call stack size exceeded while inferring conditional type. #22950

Closed

Description

TypeScript Version: 2.9.0-dev.20180328

The code below fails with a "Maximum call stack size exceeded" error. I actually came across this while trying to make a minimal example of a separate problem which is why the code looks so useless.

Code

type AProp<T extends { a: string }> = T

declare function myBug<
  T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T

const out = myBug({obj1: {a: "test"}})

Additional similar case that started crashing later. Following code does not crash in 2.8.1 but does in typescript@next

type Value<V extends string = string> = Record<"val", V>;
declare function value<V extends string>(val: V): Value<V>;

declare function ensureNoDuplicates<
  T extends {
    [K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
      ? never
      : any
  }
>(vals: T): void;

const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});

const shouldBeNoError = ensureNoDuplicates({main: value("test")});

const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});

Actual behavior:

[Error  - 09:54:19] 'quickinfo' request failed with error.
Error processing request. Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded
    at resolveEntityName (tsserver.js:22816:35)
    at getSymbolOfEntityNameOrPropertyAccessExpression (tsserver.js:40471:24)
    at getSymbolAtLocation (tsserver.js:40512:28)
    at getTypeFromTypeNode (tsserver.js:28216:34)
    at containsReference (tsserver.js:28397:79)
    at visitNode (tsserver.js:12757:24)
    at Object.forEachChild (tsserver.js:12867:24)
    at containsReference (tsserver.js:28401:27)
    at visitNode (tsserver.js:12757:24)
    at Object.forEachChild (tsserver.js:12894:24)
    at containsReference (tsserver.js:28401:27)
    at visitNode (tsserver.js:12757:24)
    at Object.forEachChild (tsserver.js:12884:24)
    at isTypeParameterPossiblyReferenced (tsserver.js:28388:31)
    at tsserver.js:27929:146
    at Object.filter (tsserver.js:1671:31)
    at getTypeFromConditionalTypeNode (tsserver.js:27929:92)
    at getTypeFromTypeNode (tsserver.js:28211:28)
    at getTemplateTypeFromMappedType (tsserver.js:26007:52)
    at substituteIndexedMappedType (tsserver.js:27818:36)
    at getSimplifiedIndexedAccessType (tsserver.js:27810:28)
    at getConstraintOfIndexedAccess (tsserver.js:26146:31)
    at getConstraintOfType (tsserver.js:26138:40)
    at getConstraintForRelation (tsserver.js:29333:80)
    at structuredTypeRelatedTo (tsserver.js:29404:38)
    at recursiveTypeRelatedTo (tsserver.js:29315:53)
    at isRelatedTo (tsserver.js:29031:38)
    at checkTypeRelatedTo (tsserver.js:28882:26)
    at isTypeRelatedTo (tsserver.js:28864:24)
    at Function.compareTypesAssignable [as compareTypes] (tsserver.js:28584:20)
    at getInferredType (tsserver.js:30913:34)
    at mapper (tsserver.js:30436:32)
    at instantiateType (tsserver.js:28464:28)
    at getConditionalType (tsserver.js:27888:60)
    at getTypeFromConditionalTypeNode (tsserver.js:27943:38)
    at getTypeFromTypeNode (tsserver.js:28211:28)
    at getTemplateTypeFromMappedType (tsserver.js:26007:52)
    at substituteIndexedMappedType (tsserver.js:27818:36)
    at getSimplifiedIndexedAccessType (tsserver.js:27810:28)
    at getConstraintOfIndexedAccess (tsserver.js:26146:31)
    at getConstraintOfType (tsserver.js:26138:40)
    at getConstraintForRelation (tsserver.js:29333:80)
    at structuredTypeRelatedTo (tsserver.js:29404:38)
    at recursiveTypeRelatedTo (tsserver.js:29315:53)
    at isRelatedTo (tsserver.js:29031:38)
    at checkTypeRelatedTo (tsserver.js:28882:26)
    at isTypeRelatedTo (tsserver.js:28864:24)
    at Function.compareTypesAssignable [as compareTypes] (tsserver.js:28584:20)
    at getInferredType (tsserver.js:30913:34)
    at mapper (tsserver.js:30436:32)
    at instantiateType (tsserver.js:28464:28)
    at getConditionalType (tsserver.js:27888:60)
    at getTypeFromConditionalTypeNode (tsserver.js:27943:38)
    at getTypeFromTypeNode (tsserver.js:28211:28)
    at getTemplateTypeFromMappedType (tsserver.js:26007:52)
    at substituteIndexedMappedType (tsserver.js:27818:36)
    at getSimplifiedIndexedAccessType (tsserver.js:27810:28)
    at getConstraintOfIndexedAccess (tsserver.js:26146:31)
    at getConstraintOfType (tsserver.js:26138:40)
    at getConstraintForRelation (tsserver.js:29333:80)
    at structuredTypeRelatedTo (tsserver.js:29404:38)
    at recursiveTypeRelatedTo (tsserver.js:29315:53)
    at isRelatedTo (tsserver.js:29031:38)
    at checkTypeRelatedTo (tsserver.js:28882:26)
    at isTypeRelatedTo (tsserver.js:28864:24)
    at Function.compareTypesAssignable [as compareTypes] (tsserver.js:28584:20)
    at getInferredType (tsserver.js:30913:34)
    at mapper (tsserver.js:30436:32)
    at instantiateType (tsserver.js:28464:28)
    at getConditionalType (tsserver.js:27888:60)
    at getTypeFromConditionalTypeNode (tsserver.js:27943:38)
    at getTypeFromTypeNode (tsserver.js:28211:28)
    at getTemplateTypeFromMappedType (tsserver.js:26007:52)
    at substituteIndexedMappedType (tsserver.js:27818:36)
    at getSimplifiedIndexedAccessType (tsserver.js:27810:28)
    at getConstraintOfIndexedAccess (tsserver.js:26146:31)
    at getConstraintOfType (tsserver.js:26138:40)
    at getConstraintForRelation (tsserver.js:29333:80)
    at structuredTypeRelatedTo (tsserver.js:29404:38)
    at recursiveTypeRelatedTo (tsserver.js:29315:53)
    at isRelatedTo (tsserver.js:29031:38)
    at checkTypeRelatedTo (tsserver.js:28882:26)
    at isTypeRelatedTo (tsserver.js:28864:24)
    at Function.compareTypesAssignable [as compareTypes] (tsserver.js:28584:20)
    at getInferredType (tsserver.js:30913:34)
    at mapper (tsserver.js:30436:32)
    at instantiateType (tsserver.js:28464:28)
    at getConditionalType (tsserver.js:27888:60)
    at getTypeFromConditionalTypeNode (tsserver.js:27943:38)
    at getTypeFromTypeNode (tsserver.js:28211:28)
    at getTemplateTypeFromMappedType (tsserver.js:26007:52)
    at substituteIndexedMappedType (tsserver.js:27818:36)
    at getSimplifiedIndexedAccessType (tsserver.js:27810:28)
    at getConstraintOfIndexedAccess (tsserver.js:26146:31)
    at getConstraintOfType (tsserver.js:26138:40)
    at getConstraintForRelation (tsserver.js:29333:80)
    at structuredTypeRelatedTo (tsserver.js:29404:38)
    at recursiveTypeRelatedTo (tsserver.js:29315:53)
    at isRelatedTo (tsserver.js:29031:38)
    at checkTypeRelatedTo (tsserver.js:28882:26)

Playground Link:
Link

Related Issues:
Multiple "Maximum call stack size exceeded" issues exist but looking at the recent ones I did not notice any that were obvious duplicates.

Activity

  1. mhegazy commented on Mar 28, 2018

    @mhegazy
    Contributor

    I am not sure why you are writing a constraint this way, it seems to me that this should just be declare function myBug<T extends Record<string, {a:string}>>(arg: T): T, on any rate, we should not crash.

  2. kpdonn commented on Mar 28, 2018

    @kpdonn
    ContributorAuthor

    Agreed the code isn’t meaningful at all. I was in the process of writing something longer that wouldn’t have been so pointless when I stumbled onto this crash and figured the crash was a bug regardless of the code.

  3. mhegazy commented on Mar 28, 2018

    @mhegazy
    Contributor

    constraining a generic type of a mapped type on itself is not that useful of a patter i would say.

  4. kpdonn commented on Mar 29, 2018

    @kpdonn
    ContributorAuthor

    You'd be surprised then. I've been using the pattern to successfully create compile errors if duplicate keys are found in an api that shouldn't accept duplicates.

    Simple-ish example I came up with from #22679:

    type ArrayKeys = keyof any[]
    type Indices<T> = Exclude<keyof T, ArrayKeys>
    type GetUnionKeys<U> = U extends Record<infer K, any> ? K : never
    type CombineUnion<U> = { [K in GetUnionKeys<U>]: U extends Record<K, infer T> ? T : never }
    type Combine<T> = CombineUnion<T[Indices<T>]>
    
    declare function combine2<
      T extends object[] &
        {
          [K in Indices<T>]: {
            [K2 in keyof T[K]]: K2 extends GetUnionKeys<T[Exclude<Indices<T>, K>]> ? never : any
          }
        } & { "0": any }
    >(objectsToCombine: T): Combine<T>
    
    const result2 = combine2([{ foo: 534 }, { bar: "test" }])
    // in TS 2.8 result2 has type {foo: number, bar: string}
    
    const error2 = combine2([{ foo: 534, dupKey: "dup1" }, { bar: "test", dupKey: "dup2" }])
    // Actually has an error here as intended because of dupKey in both objects

    Playground Link

    And it's not ready for anyone to use or even really read yet but I've been experimenting with more complicated use cases that might end up being very practical: Example

  5. Cryrivers commented on May 9, 2018

    @Cryrivers

    Here's a meaningful case that triggers Maximum call stack size exceeded in TypeScript 2.9 Dev.

    https://github.com/DefinitelyTyped/DefinitelyTyped/blob/65b176eaefc9a53c8e9cc615d290cb927947a6bb/types/react-redux/index.d.ts#L66

    tsc crashes unless the conditional type is removed.

  6. Cryrivers commented on May 23, 2018

    @Cryrivers

    Update: I believe this bug has been fixed in 2.9.1 insiders build.

  7. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions