-
Notifications
You must be signed in to change notification settings - Fork 12.8k
[Performance] Adding just one more method causes check time to jump from 2s to 100+s #31612
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
Comments
What. Why is this a thing. |
https://www.youtube.com/watch?v=3P6DWAwwViU I love this. TL;DW, There's this game where you draw "trees" to build a "forest". Each "tree" is made up of colored nodes, and non-colored edges. The game ends when certain conditions are met. TREE(1) is the most number of "trees" you can draw using 1 color. It just so happens that when you have 3 colors, you can draw a tonne of different "trees" without causing the game to end. Also, |
At this point I'm going to go out on a limb and guess that the compiler actually got caught in an infinite loop (of course I can't prove that--halting problem and all that). Why that is... I couldn't say. |
I decided to tweak the declaration a little. To use https://github.com/AnyhowStep/type-mapping/tree/ab2614abc5ee05ba4edfb863a17192b98af8a8bd Under The method declarations no longer take forever to check. Commenting out the It should be assignable but the checker seems to have problems with it. This line is meant to cause a compile error. However, the error message is really long. With interfaces,
With type aliases,
|
Is there an isolated version of this we can pull? |
I think there’s a link in the OP. |
"Isolated" meaning a single file of code |
Oh, sorry, I just saw this. I'll try and get to it. Not sure if it's worth noting at all but the repo has no external dependencies during run time. And doesn't use node libraries. No need to worry about malicious code executing |
With this, type UnionToIntersection<U> = (
(
U extends any ? (k: U) => void : never
) extends (
(k: infer I) => void
) ? I : never
);
interface Mapper<HandledInputT, OutputT> {
(name : string, mixed : HandledInputT) : OutputT,
}
type AnyMapper = (
Mapper<any, any>
);
type SafeMapper<OutputT> = (
Mapper<unknown, OutputT>
);
type AnySafeMapper = (
SafeMapper<any>
);
interface ExpectedInput<T> {
__expectedInput? : [T],
}
interface MappableInput<T> {
__mappableInput? : [T],
}
type OutputOf<F extends AnyMapper> = (
ReturnType<F>
);
type ExpectedInputOfImpl<F extends AnyMapper> = (
F extends Mapper<infer T, any> ?
(
unknown extends T ?
(
F extends ExpectedInput<infer T> ?
[T] :
F extends MappableInput<infer T> ?
[T] :
[OutputOf<F>]
) :
[T]
) :
never
);
type ExpectedInputOf<F extends AnyMapper> = (
Extract<
UnionToIntersection<ExpectedInputOfImpl<F>>,
[any]
>[0]
);
type MappableInputOfImpl<F extends AnyMapper> = (
F extends Mapper<infer T, any> ?
(
unknown extends T ?
(
F extends MappableInput<infer T> ?
[T] :
F extends ExpectedInput<infer T> ?
[T] :
[OutputOf<F>]
) :
[T]
) :
never
);
type MappableInputOf<F extends AnyMapper> = (
Extract<
UnionToIntersection<MappableInputOfImpl<F>>,
[any]
>[0]
);
interface Optional {
__optional : true,
}
type IsOptional<F extends AnySafeMapper> = (
F extends Optional ?
(
undefined extends MappableInputOf<F> ?
true :
false
) :
false
);
type IsExpectedInputOptional<F extends AnySafeMapper> = (
IsOptional<F> extends true ?
(
undefined extends ExpectedInputOf<F> ?
true :
false
) :
false
);
type DeriveMapper<
SrcKeyT extends string,
DstKeyT extends string,
F extends AnySafeMapper
> = (
& SafeMapper<
{ [dst in DstKeyT] : OutputOf<F> }
>
& ExpectedInput<
IsExpectedInputOptional<F> extends true ?
{ [src in SrcKeyT]? : ExpectedInputOf<F> } :
{ [src in SrcKeyT] : ExpectedInputOf<F> }
>
& MappableInput<
IsOptional<F> extends true ?
{ [src in SrcKeyT]? : MappableInputOf<F> } :
{ [src in SrcKeyT] : MappableInputOf<F> }
>
);
type RenameMapper<
SrcKeyT extends string,
DstKeyT extends string,
F extends AnySafeMapper
> = (
& SafeMapper<
{ [dst in DstKeyT] : OutputOf<F> }
>
& ExpectedInput<
IsExpectedInputOptional<F> extends true ?
{ [dst in DstKeyT]? : ExpectedInputOf<F> } :
{ [dst in DstKeyT] : ExpectedInputOf<F> }
>
& MappableInput<
IsOptional<F> extends true ?
(
| { [src in SrcKeyT]? : MappableInputOf<F> }
| { [dst in DstKeyT]? : MappableInputOf<F> }
) :
(
| { [src in SrcKeyT] : MappableInputOf<F> }
| { [dst in DstKeyT] : MappableInputOf<F> }
)
>
);
type FluentMapper<F extends AnySafeMapper> = (
& IFluentMapper<F>
& F
);
interface IFluentMapper<F extends AnySafeMapper> {
derive<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//Uncomment this block to warm yourself up during winter
/*
derive2<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//*/
//Uncomment this block to warm yourself up during winter
/*
derive3<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//*/
rename<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<RenameMapper<SrcKeyT, DstKeyT, F>>
);
}
declare function derive<
SrcKeyT extends string,
DstKeyT extends string,
F extends AnySafeMapper
> (
srcKey : SrcKeyT,
dstKey : DstKeyT,
f : F
) : (
DeriveMapper<SrcKeyT, DstKeyT, F>
);
/**
MODIFIES THE FUNCTION `f`!
DOES NOT RETURN A NEW FUNCTION.
*/
function fluentMapper<F extends AnySafeMapper> (f : F) : FluentMapper<F> {
const result : FluentMapper<F> = f as any;
result.derive = <
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
) => {
return fluentMapper(derive(srcKey, dstKey, f));
};
return result;
}
With
With
With And, result.derive = (<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
) => {
return fluentMapper(derive(srcKey, dstKey, f));
}); Changed to, result.derive = (<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
) => {
return fluentMapper(derive(srcKey, dstKey, f)) as any;
}) as any;
So, we have two I guess it has trouble realizing,
If you have,
It'll take 31s.
It'll take 20s.
It'll take 7s. If you have,
It'll take.... Very long. I got to play an entire song (3m28s) and it still didn't complete. Also, CPU started to go crazy, So, it seems like Just having enough copies of |
Okay, even shorter repro, removing type UnionToIntersection<U> = (
(
U extends any ? (k: U) => void : never
) extends (
(k: infer I) => void
) ? I : never
);
interface Mapper<HandledInputT, OutputT> {
(name : string, mixed : HandledInputT) : OutputT,
}
type AnyMapper = (
Mapper<any, any>
);
type SafeMapper<OutputT> = (
Mapper<unknown, OutputT>
);
type AnySafeMapper = (
SafeMapper<any>
);
interface ExpectedInput<T> {
__expectedInput? : [T],
}
interface MappableInput<T> {
__mappableInput? : [T],
}
type OutputOf<F extends AnyMapper> = (
ReturnType<F>
);
type ExpectedInputOfImpl<F extends AnyMapper> = (
F extends Mapper<infer T, any> ?
(
unknown extends T ?
(
F extends ExpectedInput<infer T> ?
[T] :
F extends MappableInput<infer T> ?
[T] :
[OutputOf<F>]
) :
[T]
) :
never
);
type ExpectedInputOf<F extends AnyMapper> = (
Extract<
UnionToIntersection<ExpectedInputOfImpl<F>>,
[any]
>[0]
);
type MappableInputOfImpl<F extends AnyMapper> = (
F extends Mapper<infer T, any> ?
(
unknown extends T ?
(
F extends MappableInput<infer T> ?
[T] :
F extends ExpectedInput<infer T> ?
[T] :
[OutputOf<F>]
) :
[T]
) :
never
);
type MappableInputOf<F extends AnyMapper> = (
Extract<
UnionToIntersection<MappableInputOfImpl<F>>,
[any]
>[0]
);
interface Optional {
__optional : true,
}
type IsOptional<F extends AnySafeMapper> = (
F extends Optional ?
(
undefined extends MappableInputOf<F> ?
true :
false
) :
false
);
type IsExpectedInputOptional<F extends AnySafeMapper> = (
IsOptional<F> extends true ?
(
undefined extends ExpectedInputOf<F> ?
true :
false
) :
false
);
type DeriveMapper<
SrcKeyT extends string,
DstKeyT extends string,
F extends AnySafeMapper
> = (
& SafeMapper<
{ [dst in DstKeyT] : OutputOf<F> }
>
& ExpectedInput<
IsExpectedInputOptional<F> extends true ?
{ [src in SrcKeyT]? : ExpectedInputOf<F> } :
{ [src in SrcKeyT] : ExpectedInputOf<F> }
>
& MappableInput<
IsOptional<F> extends true ?
{ [src in SrcKeyT]? : MappableInputOf<F> } :
{ [src in SrcKeyT] : MappableInputOf<F> }
>
);
type FluentMapper<F extends AnySafeMapper> = (
& IFluentMapper<F>
& F
);
interface IFluentMapper<F extends AnySafeMapper> {
derive<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//Uncomment this block to warm yourself up during winter
//*
derive2<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//*/
//Uncomment this block to warm yourself up during winter
//*
derive3<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//*/
//Uncomment this block to warm yourself up during winter
//*
derive4<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
//*/
}
declare function derive<
SrcKeyT extends string,
DstKeyT extends string,
F extends AnySafeMapper
> (
srcKey : SrcKeyT,
dstKey : DstKeyT,
f : F
) : (
DeriveMapper<SrcKeyT, DstKeyT, F>
);
/**
MODIFIES THE FUNCTION `f`!
DOES NOT RETURN A NEW FUNCTION.
*/
function fluentMapper<F extends AnySafeMapper> (f : F) : FluentMapper<F> {
const result : FluentMapper<F> = f as any;
//Comment out the `as any` to see it freak out.
result.derive = (<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
) => {
return fluentMapper(derive(srcKey, dstKey, f)) /**/as any;
}) /**/as any;
return result;
}
Comment out both Now, it takes forever. |
If I change, type FluentMapper<F extends AnySafeMapper> = (
& IFluentMapper<F>
& F
); to, type FluentMapper<F extends AnySafeMapper> = (
& IFluentMapper<F>
//& F
); It checks in 7s again. No clue why. So, multiple copies of |
I decided to forgo the export interface FluentMapper<F extends AnySafeMapper> {
(name : string, mixed : unknown) : OutputOf<F>;
__expectedInput? : [ExpectedInputOf<F>];
__mappableInput? : [MappableInputOf<F>];
__optional : F extends Optional ? true : false;
//== object ==
derive<
SrcKeyT extends string,
DstKeyT extends string
> (
srcKey : SrcKeyT,
dstKey : DstKeyT
) : (
FluentMapper<DeriveMapper<SrcKeyT, DstKeyT, F>>
);
} Now, I can have as many copies of |
In this example the types under consideration are.... complex: What seems to be going on is that every Fixing this may require something similar to an insight I has while working on #31633 - namely that while we're measuring weather |
@AnyhowStep nowadays we issue a |
TypeScript Version: 3.5.0-dev.20190523
Search Terms: method, generic, interface, slow check time, this
Code
Scroll to the bottom to see the repository link and reproduction steps.
For now, I'll post a rough example of what I am experiencing,
I first had something like this,
The above compiles in 2s. Nothing suspicious.
Then, I added one more method,
And then it took 102s!
This was very suspicious. I looked at the output
.d.ts
file but there was nothing out of place.In fact, emit only took 0.53s
I assumed this new method must be the culprit.
However, I decided to comment out everything but the new method,
This time, it only took 3s.
So, the problem wasn't this new method, it seemed.
I was so confused. This didn't make sense.
Then, I got to thinking, "What if it was the number of methods?"
Like, just add one more arbitrary method and see what happens.
Copy+paste
foo<>()
and rename it tofoo2
. Change nothing else,The above now compiles in 47+s. What?
I looked at the generated .d.ts file and I didn't see any explosion of types.
It looked exactly the way I predicted it should and only took up 29 lines.
So, without
foo2<>()
, it takes 2s. With it, it takes 47s.And
foo2<>()
is exactly the same asfoo<>()
Copy+paste
foo<>()
(again) and rename it tofoo3
. Change nothing else,I started this build at 22:20, 2019 May 27.
Well, it's been checking for 40 minutes and hasn't finished yet.
Expected behavior:
foo<>()
without any changes shouldn't increase check time.bar<>()
alone compiles in 3s, andx<>(), y<>(), z<>()
compile in 2s, compiling all of these methods should not take 102s.Actual behavior:
Github Link:
https://github.com/AnyhowStep/type-mapping/tree/d09851dc437aa25b702a27fa7b6eb5060dd476c5/src
npm install
npm run build
The problem is specifically in
src/fluent.ts
You'll see
derive2<>(), derive3<>()
. Uncommented, it'll take forever to check.https://github.com/AnyhowStep/type-mapping/blob/d09851dc437aa25b702a27fa7b6eb5060dd476c5/src/fluent.ts#L64
You'll see
rename<>()
. Uncommented, it'll take 100s to check.But if you comment everything else but
rename<>()
, it'll take 3s to check.https://github.com/AnyhowStep/type-mapping/blob/d09851dc437aa25b702a27fa7b6eb5060dd476c5/src/fluent.ts#L86
Related Issues:
I know there are a bunch of performance related issues around here.
I've even opened a few myself in the past.
But I'm not even sure what is related to this.
As a side note, I usually implement my libraries with just a function composition API.
Then, when I want to make it more usable, I add a fluent API on top of it.
But it seems like, with TypeScript, whenever I start to build the fluent API wrapper, I get weird problems like reaching the max instantiation depth, or super-long check times.
I guess function composition > fluent?
I feel like this problem might have to do with the fact that it's a generic interface and the return type of the methods reference the
this
type.This reminds me of
TREE(3)
.Except,
The text was updated successfully, but these errors were encountered: