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

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

Closed
kpdonn opened this issue Mar 28, 2018 · 6 comments · Fixed by #26558
Closed

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

kpdonn opened this issue Mar 28, 2018 · 6 comments · Fixed by #26558
Assignees
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue

Comments

@kpdonn
Copy link
Contributor

kpdonn commented Mar 28, 2018

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.

@mhegazy mhegazy added the Bug A bug in TypeScript label Mar 28, 2018
@mhegazy mhegazy added this to the TypeScript 2.9 milestone Mar 28, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2018

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.

@kpdonn
Copy link
Contributor Author

kpdonn commented Mar 28, 2018

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.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2018

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

@kpdonn
Copy link
Contributor Author

kpdonn commented Mar 29, 2018

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

@Cryrivers
Copy link

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.

@Cryrivers
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants