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

Excessive Stack Depth from DeepPartial/RecursivePartial in 2.7.x #21592

Closed
kevinbeal opened this issue Feb 3, 2018 · 47 comments
Closed

Excessive Stack Depth from DeepPartial/RecursivePartial in 2.7.x #21592

kevinbeal opened this issue Feb 3, 2018 · 47 comments
Labels
Bug A bug in TypeScript Crash For flagging bugs which are compiler or service crashes or unclean exits, rather than bad output
Milestone

Comments

@kevinbeal
Copy link

Search Terms:
"excessive depth"
"generic partial"

Code

const fn = <T>(arg: T) => {
    ((arg2: RecursivePartial<T>) => {
        // ...
    })(arg); // <-- here
};

type RecursivePartial<T> = {
    [P in keyof T]?: RecursivePartial<T[P]>;
};

Expected behavior:
Comparing a generic with a partial of that generic working without errors.

Actual behavior:
Getting "Excessive stack depth comparing types 'T' and 'RecursivePartial'." since upgrading to 2.7.x. (Also errors in 2.8.x). Working in 2.6.1.

Playground Link:
https://www.typescriptlang.org/play/index.html#src=class%20Greeter%20%7B%0D%0A%20%20%20%20greeting%3A%20RecursivePartial%3Cstring%3E%3B%0D%0A%20%20%20%20constructor(message%3A%20string)%20%7B%0D%0A%20%20%20%20%20%20%20%20this.greeting%20%3D%20message%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20greet()%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20%22Hello%2C%20%22%20%2B%20this.greeting%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Alet%20greeter%20%3D%20new%20Greeter(%22world%22)%3B%0D%0A%0D%0Alet%20button%20%3D%20document.createElement('button')%3B%0D%0Abutton.textContent%20%3D%20%22Say%20Hello%22%3B%0D%0Abutton.onclick%20%3D%20function()%20%7B%0D%0A%20%20%20%20alert(greeter.greet())%3B%0D%0A%7D%0D%0A%0D%0Adocument.body.appendChild(button)%3B%0D%0A%0D%0Aconst%20fn%20%3D%20%3CT%3E(arg%3A%20T)%20%3D%3E%20%7B%0D%0A%20%20%20%20((arg2%3A%20RecursivePartial%3CT%3E)%20%3D%3E%20%7B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%0D%0A%20%20%20%20%7D)(arg)%3B%0D%0A%7D%3B%0D%0A%0D%0Atype%20RecursivePartial%3CT%3E%20%3D%20%7B%0D%0A%20%20%20%20%5BP%20in%20keyof%20T%5D%3F%3A%20RecursivePartial%3CT%5BP%5D%3E%3B%0D%0A%7D%3B

Related Issues:
None?

@Conaclos
Copy link

Conaclos commented Mar 3, 2018

I got the same code with the next code:

type SafeAny <T> = {
    [k in keyof T]?: SafeAny<T[k]>
} | boolean | number | string | symbol | null | undefined

type DataValidator <T> = {
    [k in keyof T]?: (v: SafeAny<T[k]>) => v is T[k]
}

The following error is reported:

error TS2321: Excessive stack depth comparing types 'T[k]' and 'SafeAny<T[k]>'.

@kevinbeal
Your playground link is wrong (you need to hit "share" before copying the link).

@kevinbeal
Copy link
Author

@Conaclos In what sense exactly is the link wrong? I did hit "share". It wouldn't be a really long URL with url encoded typescript in the URL if I hadn't. Are you not seeing the issue when you click the link?

@Conaclos
Copy link

Conaclos commented Mar 5, 2018

@kevinbeal
Sorry, I expected the same code than the one posted here...

@sandersn
Copy link
Member

From bisecting, the bad commit is part of #19564.

sandersn added a commit that referenced this issue Mar 15, 2018
Recursive mapped types usually lead to the error "excess stack depth
comparing types" because of a new type relation rule added in #19564
which says that "A source type T is related to a target type { [P in
keyof T]: X } if T[P] is related to X".

Unfortunately, with self-recursive mapped types like

```ts
D<T> = { [P in keyof T]: D<T[P]> }
```
we get infinite recursion when trying to assign a type
parameter T to D<T>, as T[P] is compared to D<T[P]>, T[P][P] is compared
to D<T[P][P]>, and so on.

We can avoid many of these infinite recursions by replacing occurrences
of D in the template type with its type argument. This works because
mapped types will completely cover the tree, so checking assignability
of the top level implies that checking of lower level would succeed,
even if there are infinitely many levels.

For example:

```ts
D<T> = { [P in keyof T]: D<T[P]> | undefined }
<T>(t: T, dt: D<T>) => { dt = t }
```

would previously check that `T[P]` is assignable to `D<T[P]> |
undefined`. Now, after replacement, it checks that `T[P]` is assignable
to `T[P] | undefined`.

This implementation suffers from 3 limitations:
1. I use aliasSymbol to detect whether a type reference is a
self-recursive one. This only works when the mapped type is at the top
level of a type alias.
2. Not all instances of D<T> are replaced with T, just those in
intersections and unions. I think this covers almost all uses.
3. This doesn't fix #21048, which tries to assign an "off-by-one"
partial-deep type to itself.

Mostly fixes #21592. One repro there has a type alias to a union, and a
mapped type is a member of the union. But this can be split into two
aliases:

```ts
type SafeAnyMap<T> = { [K in keyof T]?: SafeAny<T[K] };
type SafeAny<T> = SafeAnyMap<T> | boolean | string | symbol | number | null | undefined;
```
@sandersn sandersn added the Fixed A PR has been merged for this issue label Mar 19, 2018
@DanielRosenwasser DanielRosenwasser modified the milestones: TypeScript 2.8.1, TypeScript 2.8.2 Mar 19, 2018
@mhegazy mhegazy modified the milestones: TypeScript 2.8.2, TypeScript 2.9 Apr 3, 2018
@chirdeeptomar
Copy link

I am using TypeScript 2.9.1 and still the same issue.

@rafaelbatistamarcilio
Copy link

@chirdeeptomar this issue was assigned to the 3.0 milestone

@mhegazy mhegazy modified the milestones: TypeScript 3.0, TypeScript 3.1 Jul 2, 2018
@sandersn sandersn modified the milestones: TypeScript 3.6.0, Backlog Jun 14, 2019
@sandersn sandersn removed their assignment Jun 14, 2019
@hswhite33
Copy link

Running into the same issue with typescript@3.5.1 and redux@4.0.1. Redux also provides a DeepPartial type.

Example:

function foo<S>(reducer: Redux.Reducer<S>, initialState: S) {
	const middleware: Redux.Middleware<{}, S>[] = [];
	const compose = window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] || Redux.compose;
	return Redux.createStore(reducer, initialState, compose(Redux.applyMiddleware(...middleware)));
}

This actually results in excessive stack depth on both the second and third params of createStore:

  • The second fails trying to compare S with DeepPartial<S>, and can be resolved by passing initialState as unknown as Redux.DeepPartial<S>
  • The third fails trying to compare any with DeepPartial<S> and can be resolved by casting compose as typeof Redux.compose

This has gotten worse in one of the more recent TypeScript versions; with typescript@3.1.6 I only needed to add as any to workaround the second parameter, and no workaround was necessary for the third.

Seems like this is a difficult problem on TypeScript's end so hopefully this workaround is useful to someone else in the meantime... Is this something I should be raising with the Redux maintainers?

@sandersn
Copy link
Member

@hswhite33 Yes, the Redux maintainers should first consider whether they need the additional type safety or inference information that comes from DeepPartial. Redux users probably don't rely on it that much, actually.

If they want to keep it, they can use the workaround above, although it might break in future Typescript versions.

siberex added a commit to scalio/typeorm that referenced this issue Jul 31, 2019
…t with typeorm.

fixes error TS2321: Excessive stack depth comparing types 'any' and 'FindConditions<T>'

More context on the issue:
microsoft/TypeScript#21592 (comment)

Related PR: typeorm#4470
iRON5 pushed a commit to iRON5/DefinitelyTyped that referenced this issue Aug 13, 2019
…#35848)

* Remove PartialDeep from lodash, replace w/Partial

Fixes the most common occurrence of microsoft/TypeScript#21592. I don't
think anybody uses the full capability of `PartialDeep` as a parameter,
and it doesn't really make sense as the return type of `pick`.

* Use PartialObject name instead of GlobalPartial
@Yaojian
Copy link

Yaojian commented Aug 15, 2019

I bet a $ that we will see a message: sandersn (or whoever) modified the milestones: TypeScript 3.5.0, TypeScript 3.6.0 some day. 😿

I won but disappointed. 😭

Now my questions is: Will it be moved to 3.7.0 😿 ? Should I bet 10$ ?

raymondfeng added a commit to loopbackio/loopback-next that referenced this issue Aug 28, 2019
error TS2321: Excessive stack depth comparing types 'DataObject<?>' and 'DataObject<?>'.
See microsoft/TypeScript#21592
@alfaproject
Copy link

Just got hit by this in TS 3.6.2 which Renovate bot just attempted to update to and our pipeline failed. ):

It's not even our own code. It comes from one of the libraries that we use:

error TS2321: Excessive stack depth comparing types 'ElemMatch<?>' and 'ElemMatch<?>'.
error TS2321: Excessive stack depth comparing types 'InternalQuery<?>' and 'InternalQuery<?>'.
error TS2321: Excessive stack depth comparing types 'SiftQuery<?>' and 'SiftQuery<?>'.

https://github.com/crcn/sift.js/blob/ef8c5ca2e4b4277cf79085dd4168dbbe58ea4869/index.d.ts

raymondfeng added a commit to loopbackio/loopback-next that referenced this issue Aug 29, 2019
error TS2321: Excessive stack depth comparing types 'DataObject<?>' and 'DataObject<?>'.
See microsoft/TypeScript#21592
@lukejagodzinski
Copy link

Is it a bug in TypeScript compiler or just libraries' code is faulty? Maybe TS team should come up with some guidance how to write code to avoid this bug if it's not an issue with tsc itself. It's really frustrating that it didn't get fixed yet

bajtos pushed a commit to loopbackio/loopback-next that referenced this issue Sep 3, 2019
error TS2321: Excessive stack depth comparing types 'DataObject<?>' and 'DataObject<?>'.
See microsoft/TypeScript#21592
bajtos pushed a commit to loopbackio/loopback-next that referenced this issue Sep 3, 2019
error TS2321: Excessive stack depth comparing types 'DataObject<?>' and 'DataObject<?>'.
See microsoft/TypeScript#21592
@sandersn
Copy link
Member

sandersn commented Sep 4, 2019

@alfaproject you have a different issue. Notice that the two types are the same in your case. This is a known bug (#33132) with a fix at #33144. We'll ship it in 3.6.3 in the next week or so.

@tomas-wrobel
Copy link

I hate this bug. The only thing is work agains it is // @ts-ignore

imnotjames added a commit to imnotjames/typeorm that referenced this issue Jul 31, 2021
this was added to our typing to work around issue described in
microsoft/TypeScript#21592 - however, as of TS 3.6.3 it has not been
an issue
imnotjames added a commit to imnotjames/typeorm that referenced this issue Jul 31, 2021
this was added to our typing to work around issue described in
microsoft/TypeScript#21592 - however, as of TS 3.6.3 it has not been
an issue
imnotjames added a commit to typeorm/typeorm that referenced this issue Jul 31, 2021
this was added to our typing to work around the issue described in
microsoft/TypeScript#21592 - however, as of TS 3.6.3 it has not been
a problem
@sandersn
Copy link
Member

sandersn commented Nov 5, 2021

Fixed in 3.7

@sandersn sandersn closed this as completed Nov 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Crash For flagging bugs which are compiler or service crashes or unclean exits, rather than bad output
Projects
None yet
Development

Successfully merging a pull request may close this issue.