I'm investigating why the compilation time for my Angular 2.0 TypeScript project went from around 4 seconds to around 15 seconds in a relatively short time.
I came across the very useful, but seemingly undocumented --diagnostics
switch.
For example, here's what I get when running tsc --noEmit --diagnostics
on my project now:
Files: 231
Lines: 50872
Nodes: 170067
Identifiers: 65994
Symbols: 7712123
Types: 407677
Memory used: 600554K
I/O read: 0.43s
I/O write: 0.00s
Parse time: 1.13s
Bind time: 0.34s
Check time: 10.17s
Emit time: 0.00s
Total time: 11.64s
Here's what I get when I run the same command on an earlier version of the project.
Files: 197
Lines: 30882
Nodes: 124208
Identifiers: 46201
Symbols: 5856945
Types: 10989
Memory used: 80412K
I/O read: 0.03s
I/O write: 0.00s
Parse time: 0.60s
Bind time: 0.27s
Check time: 0.93s
Emit time: 0.00s
Total time: 1.79s
The number of Types
has gone way up, and so has the Check time
.
Is it possible to get more detailed/verbose output from --diagnostics
?
NodeJS v4.4.3, TypeScript v1.8.10. This is my tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"noImplicitAny": false,
"noEmitOnError": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"removeComments": false
},
"exclude": [
"node_modules",
"wwwroot",
"typings/main.d.ts",
"typings/main"
]
}
Using the built-in TypeScript compiler (tsc), it takes an average of 13.37 seconds to compile the project.
Situations Where TypeScript Takes the Cake At the time of deployment, TypeScript can point out the compilation errors, thereby minimizing the errors during run-time. On the other hand, JavaScript is an interpreted language and can point out the errors only during the run-time.
Seems I have found the culprit in my case. I did it the hard way; my process:
Before the offending commit, I consistently got compile times of around 2-4 seconds, after the commit - 13-17 seconds.
In my case, I have a class, with a accessTokenGetter
field, which was initialized in the constructor:
export class JwtConfig {
//...
accessTokenGetter: () => Observable<string>;
//...
constructor(private config?: IJwtConfig) {
// ...
this.accessTokenGetter = this.config.accessTokenGetter || (() => Observable.of(null));
}
}
The second part of the initialization || (() => Observable.of(null));
was causing the slowness. Commenting it out or adding a type annotation got the compile time back down. Since Observable is generic, it seems like the TypeScript compiler needs a hint to narrow down some type checks it needs to do. My initialization now reads as:
//...
this.accessTokenGetter = this.config.accessTokenGetter || (() => Observable.of(<string>null));
//...
Observable.of(null as string))
also seems to do the job. There were a couple of other places where adding a type annotation sped compilation up.
Hope this helps someone.
Still, if there's a facility in the compiler to the answer faster - I'd be happy to hear it.
I could speed up the compile process from 15 sec. to 6-7 sec. by changing this single line of code:
// slow:
// ...
.flatMap((receivedObj: MyType) => {
let nextObservable: Observable<MySecondType> = this.dependingPut(receivedObj);
return nextObservable || new Observable((observer) => {
observer.next(undefined);
});
});
// fast:
.flatMap((receivedObj: MyType) => {
let nextObservable: Observable<MySecondType> = this.dependingPut(receivedObj);
return nextObservable || new Observable<MySecondType>((observer) => { // <--- use the generics!
observer.next(undefined);
});
});
From the Typescript handbook (https://www.typescriptlang.org/docs/handbook/generics.html):
function identity<T>(arg: T): T {
return arg;
}
// ...
let output = identity("myString"); // type of output will be 'string'
"Notice that we didn’t have to explicitly pass the type in the angle brackets (<>), the compiler just looked at the value "myString", and set T to its type. While type argument inference can be a helpful tool to keep code shorter and more readable, you may need to explicitly pass in the type arguments as we did in the previous example when the compiler fails to infer the type, as may happen in more complex examples."
In my case it didn't fail; the type inference just took a very long time (by the way, it consumed plenty of memory as well). Before you start to build workarounds, go back in your revision history and try to identify the bad revision. To be sure that the compiler is the culprit, use the --diagnostics option. If the returned statistics returns a high "Check time" value, then inspect your code for missing types.
For me, slowdowns were due to imports like import "./file.ts";
. Removing the .ts
extension makes things 90% faster: import "./file";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With