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
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?
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).
@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?
@kevinbeal
Sorry, I expected the same code than the one posted here...
From bisecting, the bad commit is part of #19564.
I am using TypeScript 2.9.1 and still the same issue.
@chirdeeptomar this issue was assigned to the 3.0 milestone
This was actually shifted from 3.0 to 3.1 milestone. 😢
Should the "Fixed" label be removed? This appears to still be a problem, especially given that the milestone keeps moving.
Any updates on this? I came across this issue today with 3.1..
For me, this error happens when tsc
is invoked in npm scripts indirectlly using yarn
:
tsc --build
directly in shell: NO ERROR."compile": "tsc --build",
by executing yarn compile
in shell: NO ERROR."build": "yarn clean && yarn compile"
by executing yarn build
in shell: !!!ERROR!!!.any ideas?
Ran into this on 3.2.1.
This only seems to happen when i'm using yarn for some reason - we've had to lose the typings for lodash as a workaround until this is fixed as we can no longer detach from yarn.
I have the same problem when updated to 3.2.2. I have to use require("lodash")
to bypass the type validation. BTW I'm using npm 6.5
I had to disable type checking altogether by casting T to any.
Any joy with this issue. It still happens on v 3.2.2
[email protected] works fine, [email protected] fails
C:devservernode_modulests-nodesrcindex.ts:261
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
error TS2321: Excessive stack depth comparing types 'any' and 'ListIterateeCustom
error TS2321: Excessive stack depth comparing types 'any' and 'ObjectIterateeCustom
I'm currently seeing this on 3.2.2:
error TS2321: Excessive stack depth comparing types 'T' and 'DeepPartial<T>'
Also, it seems I can't cast to any
to avoid the issue:
error TS2321: Excessive stack depth comparing types 'any' and 'DeepPartial<T>'.
export declare type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
Retested with [email protected]
C:devservernode_modulests-nodesrcindex.ts:261
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
error TS2321: Excessive stack depth comparing types 'any' and 'ListIterateeCustom
error TS2321: Excessive stack depth comparing types 'any' and 'ObjectIterateeCustom
i can confirm, that this issue still exists in typescript 3.2.4 (latest)
I'm also having this issue with Typescript 3.2.4 while using TypeORM 0.2.12 and NestJS 5.4. I'm just trying to create a generic class to provide basic CRUD operations to my models, the class is pretty simple:
@Injectable()
export abstract class BaseRepository<T> {
protected constructor(
@InjectRepository(T) protected readonly entityRepository: Repository<T>,
) {}
save(entity: T | T[], options: SaveOptions = {}): Observable<T> {
return from(this.entityRepository.save(entity, options));
}
update(
criteria: FindConditions<T>,
entity: Partial<T>,
options: SaveOptions = {},
): Observable<T> {
return from(this.entityRepository.update(criteria, entity, options));
}
delete(
criteria: FindConditions<T>,
options: RemoveOptions = {},
): Observable<DeleteResult> {
return from(this.entityRepository.delete(criteria, options));
}
findById(
id: string | number,
options: FindOneOptions<T> = {},
): Observable<T> {
return from(this.entityRepository.findOne(id, options));
}
findManyById(
ids: string[] | number[],
options: FindManyOptions<T> = {},
): Observable<T[]> {
return from(this.entityRepository.findByIds(ids, options));
}
}
The exact error message is: error TS2321: Excessive stack depth comparing types 'T' and 'DeepPartial<T>'.
Update: downgrading to 3.1.6 serves as a workaround.
This problem exists in Typescript 3.2.
I have the same problem in Typescript 3.3
Also suffering this issue on TS 3.3, It happens on any Models I define using sequelize-typescript that have associations to other models e.g.
@Table
export class User extends Model<User> {
@Column({
type: DataType.UUID,
primaryKey: true,
})
id: string;
@Column(DataType.STRING(255))
identity: string;
@Column(DataType.TEXT) metadata: string;
@HasMany(() => Project)
projects: Project[];
}
Gives me the error:
Excessive stack depth comparing types '() => typeof Project' and 'ModelClassGetter'.ts(2321)
Changing the @HasMany(() => Project)
line to @HasMany(() => Project as any)
avoids the error
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. 😿
Indeed... quite infuriating. I'm currently using a postinstall script that blasts away any lodash typings in my dependencies. Gnarly.
I'm trying to get this bug up to date since it's been open a long time. Sorry for the random pings everybody is going to get.
Here's the current status, pre-cleanup: I created a PR more than a year ago with a not-very-good fix for this bug, but we didn't give it high priority because self-recursive mapped types are not a good idea. At the time, we thought they weren't used much, but they do show up in a few high-profile packages, lodash in particular.
string
have methods that come from their apparent types (eg String
) so a recursive mapped type recurs right down through these methods, even though that's never the intended meaning. With conditional types (which didn't exist when this bug was created), it's now possible to specify termination conditions for the recursion, which leads to problem 2...When checking higher-order types -- types that have type parameters inside them -- Typescript doesn't have a smart solver or anything that can simplify interesting patterns. It's just good old recursion through a series of rules that may simplify locally but have no global knowledge. Self-recursive mapped types that get assigned "one level off", like T ==> Rec<T>
, can only try to expand the mapped type over and over again because T
is a type parameter.
What you need is a global way to prove that Rec<T> ==> Rec<Rec<T>>
implies that T ==> Rec<T>
. But Rec<T> ==> Rec<Rec<T>>
is hard to recognise in the current compiler. My PR introduced an ad-hoc check for it, but I'm still not convinced that it correctly proves that T ==> Rec<T>
.
So I'm going to re-discuss the fix with other members of the team and in the meantime find popular packages that use self-recursive mapped types to see if they can be rewritten to not use them.
@kevinbeal @Conaclos @keawade Can you provide a bit more context on RecursivePartial
/DeepPartial
and SafeAny
? What is some real code that would have used these types? What did you do instead? What were you using before?
@Yaojian it sounds like tsc
is your globally installed typescript, whereas yarn compile
gives a local typescript. Do you still see this difference? Can you check typescript versions of tsc
versus yarn compile
if so?
@mtford90 @shaunxu @vitaliusvs Do you still have problems with lodash if you upgrade to @types/[email protected]
or newer? That change improved memory usage quite a bit, although it does not get rid of the DeepPartial type. If you still have problems with lodash, could you give an example that fails?
@alessandrojcm @wyqydsyq Thanks for the repros. I'll take a look and let you know if I can't make it fail on my machine.
Everyone else:
@chirdeeptomar @mdekrey @rafaelbatistamarcilio @nerder @murbanowicz @ryuuji3 @chukwunomso @bryanrideshark @andreymaznyak
Can you give me an example of the code that you saw the failure with, and what package it happened with?
Update: after some discussion, we're not any closer to solving the underlying problem.
@alessandrojcm Your error comes from typeorm; they found a workaround: adding a conditional type in the self-referential mapped type. Here's a standalone example with a nonsensical conditional type:
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends never ? DeepPartial<T[P]> : DeepPartial<T[P]>
};
declare class Repo<U> {
save(entity: DeepPartial<U>): void;
}
function save<T>(entityRepository: Repo<T>, entity: T) {
entityRepository.save(entity);
}
This is unlikely to work forever since someday the compiler will probably understand conditional types better, but is a decent workaround for now. (typeorm's conditional type is less obviously a workaround than the example above.)
However, I think most users of typeorm would be fine with just Partial
.
@wyqydsyq Your example doesn't include Project
so I couldn't get your error to repro. Also I'm not sure what version of sequelize-typescript you are using.
@sandersn I had my @types/lodash updated to 4.14.129 and the error was still there
@sandersn I was using _.sortBy(..)
in [email protected] and that was triggering the failure. The method signature was:
sortBy<T>(
collection: List<T> | null | undefined,
...iteratees: Array<Many<ListIteratee<T>>>
): T[];
I was passing an any as the second parameter and got the errors:
Error:(108, 2) TS2321: Excessive stack depth comparing types 'any' and 'Many
Error:(108, 2) TS2321: Excessive stack depth comparing types 'any' and 'Many
@sandersn Retested with [email protected] and @types/[email protected]:
C:...servernode_modulests-nodesrcindex.ts:228
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
error TS2321: Excessive stack depth comparing types 'any' and 'ListIterateeCustom
error TS2321: Excessive stack depth comparing types 'any' and 'ObjectIterateeCustom
I was using RecursivePartial to annotate mocks of services. I didn't want to build out something with the whole shape at every level. So something like this:
{ a1: { b1: { c1: {} }, b2: { d1: {} } } }
...when the only thing I'm actually testing with is that d1
property--I didn't want to also add b1
, c1
, etc. Something this trivially small is okay, but larger objects would be a pain, especially considering the following.
Neither did I want to add an annotation for every level. Something like this:
{ prop: { something: 1 } as Partial<T['prop']> } as Partial<T>
i.e. I didn't want to have to add Partial<T['prop']>
.
The workaround you (@sandersn) showed would work in my case. Thank you :)
@pietschy Here's what I tried with 4.14.132:
function f(a: any) {
return _.sortBy([1,2,3], a)
}
But it seems fine. Can you try your code with @types/[email protected]
?
@shaunxu @vitaliusvs Can you give short examples of the failing code? Was it similar to the above call to sortBy
?
Retested with [email protected] and @types/[email protected]:
function test< T >(match) {
const items: T[] = undefined;
return _.filter(items, match);
}
C:...servernode_modulests-nodesrcindex.ts:228
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
error TS2321: Excessive stack depth comparing types 'any' and 'ListIterateeCustom
error TS2321: Excessive stack depth comparing types 'any' and 'ObjectIterateeCustom
Also retested with [email protected] and @types/[email protected] and still failing.
error TS2321: Excessive stack depth comparing types 'any' and 'Many If you'd like access to the source let on bitbucket me know.
error TS2321: Excessive stack depth comparing types 'any' and 'Many
Thanks, @vitaliusvs. Turns out the missing ingredient was that the first argument needs to be generic.
I'm sceptical that anybody actually relies on DeepPartial in lodash so I'm going to propose removing it and see what the maintainers of the types think.
Thanks, @types/[email protected] has fixed this issue with lodash.
This error goes away after updated to @types/[email protected]
thanks @vitaliusvs
Running into the same issue with [email protected]
and [email protected]
. 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
:
S
with DeepPartial<S>
, and can be resolved by passing initialState as unknown as Redux.DeepPartial<S>
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 [email protected]
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?
@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.
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$ ?
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
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
@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 https://github.com/microsoft/TypeScript/pull/33144. We'll ship it in 3.6.3 in the next week or so.
Most helpful comment
I'm also having this issue with Typescript 3.2.4 while using TypeORM 0.2.12 and NestJS 5.4. I'm just trying to create a generic class to provide basic CRUD operations to my models, the class is pretty simple:
The exact error message is:
error TS2321: Excessive stack depth comparing types 'T' and 'DeepPartial<T>'.
Update: downgrading to 3.1.6 serves as a workaround.