-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[API Question] Is there a type resolution depth limit difference between compiling and running the language service? #29511
Comments
The other weird thing is that part of my code looks like this, {
[index in Extract<keyof ReturnType<SelectDelegateT>, string>]: (
ReturnType<SelectDelegateT>[index] extends IColumn ?
(
ReturnType<SelectDelegateT>[index] extends ColumnUtil.FromColumnRef<ColumnRefUtil.FromQueryJoins<QueryT>> ? never :
["Invalid IColumn", ReturnType<SelectDelegateT>[index]]
) :
never
)
} And the red squiggly line gives me Intuitively, to me, If
And if the above two are The other weird thing is that I tried to shift some types around to change the error output and I get,
After the But the fact that it thinks the type is I'm so confused =( |
Just updated to TS 3.3.0-dev.20190119 It seems like the type resolution depth limit is the same as the one in the language service now because even Now I just gotta' figure out how to increase this limit =/ |
So, I just shifted some code around and it got rid of that Before: export function isEnabled () {
return o.and(
t.merchantEnabledLog.latestValueOfEntity(
c => c.enabled
),
business.isEnabled(),
appPlatform.isEnabled(),
payOutMethod.isEnabled(),
);
} After: export function isEnabled () {
return o.and(
o.and(
t.merchantEnabledLog.latestValueOfEntity(
c => c.enabled
),
business.isEnabled(),
appPlatform.isEnabled(),
),
payOutMethod.isEnabled(),
);
}
The resultant type for both "Before" and "After" is the same. |
I'm also having this weird problem where VS code highlights folders and files in red (to say they contain errors). And when I open the files, VS code underlines some text with red squiggly lines, initially. Hovering over the lines gives me TS errors, all related to types not resolving properly (Becoming But, after a few moments (seconds to minutes), VS code removes the red squiggly lines. I assume because it types checks successfully. Closing the files makes VS code highlight those folders and files again after a few moments (seconds to minutes). Makes me think that the depth limit is different for "background" vs "foreground" type checking. If that makes any sense at all. |
I think I'm most definitely sure it has to do with some depth limit. I have a bunch of calls to functions that are generics and rely on type inference. declare function and</*Some Generic Type Params*/> (.../*Some Params*/) : /*Some Return Type*/;
function isEnabled1 () {
return and(
/*expr*/,
/*expr*/
)
}
function isEnabled2 () {
return and(
isEnabled1(),
/*expr*/,
/*expr*/,
/*expr*/
)
}
function isEnabled3 () {
return and(
isEnabled2(),
/*expr*/,
/*expr*/
)
}
/*etc.*/ The return type of all these functions resolves fine on their own. But when I start doing stuff like, /*expr*/.select(() => [isEnabled3()]) And |
I'm also having this problem where, on occasion, types are getting inferred as But they always appear when I run At this point, I'd be happy to mail my code to someone to look at what I'm doing wrong =/ |
I think it's got to do with some kind of iterative type resolution thing? For example, .where(canStartCreate)
I guess when I run @weswigham Sorry I'm tagging you directly but this is a little frustrating for me. I'm not too familiar with compiler internals, let alone |
I finally got it to compile successfully! So, after a lot of random Ctrl+F > I changed Before the change, it failed in
I ran into a lot of dead-ends trying to bump the flow depth limit, constraint depth limit, etc. But it seems like I'm pretty happy with this because v2 of a library I worked on would take [EDIT] Changed the limit to Files: 3895
Lines: 130128
Nodes: 823248
Identifiers: 257678
Symbols: 702177
Types: 249365
Memory used: 628652K
I/O read: 3.74s
I/O write: 1.51s
Parse time: 15.28s
Bind time: 2.30s
Check time: 49.50s
Emit time: 54.96s
Total time: 122.05s I'm going to keep trying to decrease the limit until I get compile errors again. |
😮 How many LoC is this project? 50s check time and 54s emit time is uuuge. |
I've noticed that the
Would be nice if we could make this configurable with a I just noticed you asked me about LoC, I'll respond in a second :x |
Here's https://github.com/AnyhowStep/typed-orm/tree/v3 You can download the project and run It's an experiment in compile-time structurally-safe MySQL query building ;D It takes about 40s to compile that library on my laptop. That library doesn't suffer from this instantiation depth problem because I never wrote compile-time tests for nested MySQL expressions more than 2 or 3 levels deep. However, in another project I'm working on (for work), it has MySQL expressions that are 5-6 levels deep, using the I think |
I think the emit time is really long because the generated types are really large. Not because I have many lines of code :x For example, this is a compile-time test input file, 14 lines of code. The output is here, 81 lines of code. [EDIT] This project using the [EDIT] Brought the limit down to Gonna' keep going lower. [EDIT]
[EDIT]
[EDIT]
It seems like |
In addition to being able to configure the max Then again, I'm not even sure how many people have realistically run into this problem to make this a worthwhile endeavor =( |
It came up once before when someone was nesting 200-some conditional types deep because they'd written what amounted to an else-if block of every syntax kind in TS. They just worked around the limit by reordering their type, though (so they had 200 top-level conditionals in a union instead of 200 levels of nesting). |
I just looked into adding a compiler option and it looks... Complicated ._. I thought it was as simple as,
But then I thought,
I found It seems like And it looks like Not too sure how a [EDIT] I just tested my changes and they seemed to work fine. Without setting |
It seems like @ahejlsberg has added an error message in #29435 at checker.ts L10977 for when the max instantiation depth gets exceeded. |
Yes, with #29435 we'll be issuing an error when and if we hit the instantiation depth limiter. Hitting is an indication that either (a) you have an infinitely recursive type or (b) you have very complicated types and are at the limit of we can reasonably support (which seems to be supported by the check time numbers you're seeing). I'm very reluctant to expose the instantiation depth as a configurable parameter as it is an implementation detail that could change. Plus it is next to impossible to explain and reason about what value it should have. I suggest you try compiling using the |
I'm at the limit but it still works ;D (with a enough depth, and time)
Ack, I hadn't considered that =/ I'll try that branch and see what I can do to stay under |
I finally went ahead and decided to use 3.4.0-dev.20190209, and get weird errors. Guess I'll be up figuring them out. The call to,
works fine outside of the |
My current workaround is, const expr = t.businessPayOutMethodEnabledLog.latestValueOfEntity(
c => c.enabled
);
return o.ExprUtil.as(
expr,
"selfEnabled"
); And it compiles. |
Another weird thing, The following compiles. return o.ExprUtil.as(o.and(
business.isEnabled(),
payOutMethod.isEnabled()
), "ancestorsEnabled"); But the following gets the return o.and(
business.isEnabled(),
payOutMethod.isEnabled()
).as("ancestorsEnabled"); However, the second way is literally just a wrapper for the first one. They both return a type of |
And the new version also broke one of my types that worked before, related to #29133 I have this type called But it seems like it doesn't work anymore. |
I finally got around to checking where my instantiation depth was going crazy because I was finally getting 800-ish seconds compile times =P Updating to TS 3.4 gave me 300+ errors. It seems like most of my problems come from using However, class Expr {
as (alias) {
return ExprUtil.as(this, alias);
}
} I've noticed a similar thing before where using classes+methods gave me a lot of problems with type-checking/compile-times while using interfaces+functions worked far better. That's why I have And every class method is just a wrapper around functions that take interfaces as arguments. I'm not sure why this is the case. |
Another big contributor is that As<> = Expr<> & IExprSelectItem<>; When I replaced the class However, replacing with the interface means I can no longer use that fancy "fluent API" design, though. |
After a lot of... Shifting function calls around (about 200-ish of them), and reverting to TS 3.3, it finally type checks successfully in two minutes!
I'm so happy T^T |
So, after 3 days and more code, it started taking 8 minutes to do a full build. I decided to experiment some more and check what's giving me these issues. It seems like when I write expressions/sub-queries that require many-table joins, I end up with long check times. For example, import * as o from "typed-orm";
const manyJoinExpression = o.and(
dao.merchant.isEnabled(),
dao.business.isEnabled(),
dao.payOutMethod.isEnabled(),
/* snip */
);
//merchant.ts
export function isEnabled () {
return o.and(
t.merchant.columns.enabled, //Requires merchant table
user.isEnabled(), //Requires user table
/* snip */
)
} At the end of it all, the emitted In the below screenshot, you'll see a lot of I decided to refactor about 7 of these long expressions to only require one table join but to use sub-queries to get access to the other tables. const expr1 = o.exists(o.requireParentJoins(tableA)
.from(tableB)
.where(o.innerJoinPk(tableA, tableB).eq)
.where(dao.tableB.isEnabled)
);
const expr2 = o.exists(o.requireParentJoins(tableA)
.from(tableC)
.where(o.innerJoinPk(tableA, tableC).eq)
.where(dao.tableC.isEnabled)
);
/* snip */
const longExpr = o.and(
expr1, expr2, expr3, expr4, /* snip */
); So, for this The emitted type will only list The result is that it now takes roughly 2.5 minutes to type check Even though both queries have the same effect, their compile times are very different ._. It feels like that regex quote,
In my case, I had a problem that writing SQL queries is generally not type-safe. Using I decided to write a type-safe query builder. Now, I have more problems. |
After refactoring one query that was joining 5 tables, the check time is After refactoring a second query that was joining 6 tables... No improvement. After refactoring a third query that was joining 5 tables... No improvement. I had to wait about 5-20s for them but they did come up. After a fourth query with 7 tables... No improvement. After a fifth query with 7 tables... No improvement. At the end of it all, it took The overall build time has improved somewhat,
Before refactoring,
|
Hi @ahejlsberg @weswigham @AnyhowStep, I recently published an article about how to create types for curry and ramda. Quite a few people are excited and waiting for me to add these types to DefinitelyTyped. But I can't pass the lint tests yet. https://medium.freecodecamp.org/typescript-curry-ramda-types-f747e99744ab To solve these kind of problems, I detail how I make use of recursive types. Indeed TS 3.4.0-dev.20190302 is breaking these types. In the latest stable version (3.3.3333) warnings arise (only) when we recurse more than 45 times. A recursive type then returns In TS 3.4.0-dev.20190302 it appears that "Type instantiation is excessively deep and possibly infinite", which is a breaking behaviour. But in fact it is only possibly infinite, and this is why the previous behaviour should be preferred. I referenced this issue in a new ticket #30188 |
I haven't moved any of my code to TS 3.4 because it also breaks a lot of my code. I don't have the time to look through the hundreds of new errors that popped up. Maybe some day =/ That said, having the compiler introduce an It was very confusing for me to see types resolved as Silently introducing Also, cool article. Have you used it on a large-ish project? Does it impact compile times? I remember experimenting with tuple type concatenation, push, pop, etc. and compile-time math with recursive types but it made compile times ridiculously slow in large projects (where I initially had an 18 minute compile time). But this was quite some time ago. |
@AnyhowStep No compile problems so far, it's fast! And to come back to what you said, it would indeed be nice to be able to set that recursion limit. In TS 3.3 it is set to 45 and in TS@next it is set to 42. I would come with an in-between solution. A limit value could be defined in tsconfig.json to increase the recursion count. If exceeded, |
Just thought I'd update this issue with a workaround to avoid reaching the max instantiation depth. This workaround will not always apply to your situation but it can be useful.
WorkaroundThis workaround abuses "composite projects".
Now, in your original project, you may use The idea behind the workaround is that resolving each type involves a certain amount of "depth currency". If it costs too much of this currency to resolve the type, TS complains. When building export const complexType : UnexpandedType<Foo>; Instead, the type of export const complexType : {
my : {
ridiculously : {
complex : Foo,
}
},
someArray : [1,2,3,Foo,"blah"],
/*snip 100+ lines and objects nested 7 levels deep*/
}; Since Before workaround,
After workaround,
Your mileage will depend on how much you can get TS to expand the type when emitting There are ways to get TS to expand the type for you during compile-time, you'll have to experiment or look for them elsewhere, though. I don't have a definitive guide for that. |
@pirix-gh It looks very tempting to try. |
I just had a random thought. Since the Instead of I probably shouldn't even be using the word If not |
Just adding a link for me to personally keep track of limits related to type instantiation, |
Just adding more notes to myself. It seems like someone else asked for higher depth limits/customizable depth limits wayyyyy before me. |
@AnyhowStep FWIW the style of super-long and multiple updates is very confusing from our side of the fence - it's not clear when you're "done", which comments have been superseded by later content, and it's not at all obvious which comment is the one that summarizes the situation. I'm going to close this; if you have a concise and concrete thing to work from that demonstrates some kind of manifest bug, we're of course happy to take a look at a fresh issue. Thanks again! |
TS 3.3.0-dev.20190103
I don't exactly have a minimal repro but I figured I'd ask my question first.
I'm experiencing a problem that I can only think is because TS is "giving up" on resolving the type in the IDE. But it resolves the type correctly during compile time.
IDE
I'm currently seeing this in my IDE,
And it seems to think that the following type,
ReturnType<SelectDelegateT>[index]
isany
.It also thinks that
ReturnType<SelectDelegateT>
isany
.However, when I just use
SelectDelegateT
, it gives me the full type, which is correct.Compile-time
It was really weird because I was pretty sure I shouldn't get an error. I gave up on the IDE and decided to run
tsc
. I configured it to havenoEmitOnError : true
.So, if there really is an error, it should spit out the same error as the IDE and not emit any types.
However,
tsc
ran fine. No errors.I checked the emitted
.d.ts
file and the emitted type was resolved correctly.So... Is my hunch right? Is there a "depth limit" difference between the language service that runs for the IDE (VS Code) vs the compiler?
And if so, is there a way for me to make it so that both have the same limit?
If possible, I'd like to make the limit as large as possible for both VS code and during compile-time.
The text was updated successfully, but these errors were encountered: