/ WRITING SPEAKING
← back

TypeScript Performance: Going Beyond The Surface

· at BeJS Conf · Brussels 🇧🇪

Length: 25 min

Abstract

Do you ever ponder how to identify and address performance issues in TypeScript to maximize the effectiveness of your code? If so, join us for a talk on the performance of TypeScript and the techniques you can use to get the most out of your code. We’ll delve into various ways to debug performance, explore how to leverage the power of the TypeScript compiler to detect potential performance issues and use the profiling tools available to track down the underlying bottlenecks.

Talk Notes

Link to GitHub with examples.

What compiler compiles

CleanShot 2023-05-11 at 06 30 15@2x

TypeScript program

An object that has all the compilation context.

Scanner

Scans the source code and convert it into a list of tokens.

CleanShot 2023-05-11 at 06 31 20@2x

Parser

Brings context to the scanner. For example: it sees a FunctionKeyword so it knows it’s gonna be a FunctionDeclaration and then if there’s an Identifier, it’s gonna be this function Identifier, its name, and then we’re gonna have parameters, and so on.

In reality, there’s some back and forth communication between parser and lexer. Parser is responsible for creating a correct AST, but it asks scanner to do some extra reading. Parser controls the scanner.

CleanShot 2023-05-11 at 06 31 40@2x

Binder

It results in errors about the whole context.

Main responsibilities:

  • Creates a symbol table — additional metadata for each node. It will be used for later phases. Has information on where identifiers are defined — scopes. Keeps track of which scope you’re in (makes go to definition in IDE work).
  • Sets up parent on syntax noes. Later checker can go up if needed and investigate the nodes above to get proper type.
  • Makes flow nodes — TS needs to keep track of scopes, what types occur where and where possible mutations occur.
  • Validates script vs module conformance.

It’s a single run through the entire tree.

CleanShot 2023-05-11 at 06 32 39@2x

Checker

Includes most of the diagnostics. It’s huge. For everything in AST there are checking functions like checkVariableStatement, checkGrammarVariableDeclarationList, isTypeRelatedTo.

Two major responsibilities:

  1. It checks if things are assignable to other things.
  2. Type inference — if there are “gaps”, it tries to fill them — this is why we store so much information with a binder.

Transformer

It takes the AST that we have and to get JavaScript code, it strips all the types and optionally applies some transformers to e.g. support modern syntax.

When creating an AST, TS keeps track of all the transformers that’s gonna be needed. E.g. if it sees an ES2018 token and the target it older, it will know that an ES2018 transformer will be needed.

And to get declaration files, it strips the code bit. DTS Transformer often asks the type checker about the types. Especially when some variables are not annotated.

Emitter

We are getting the files that we requested.

CleanShot 2023-05-11 at 06 29 52@2x

Debugging

—diagnostics/—extendedDiagnostics

CleanShot 2023-05-11 at 06 33 07@2x

It’s quite useful to see what’s going on — what compiler steps are taking significant amount of time.

The three most expensive steps are usually parsing, binding, and a checker.

—listFiles

$ tsc --noEmit --listFiles | xargs stat -f "%z %N" | npx webtreemap-cli
$ tsc --noEmit --listFiles | xargs stat -f "%z %N" | npx webtreemap-cli

—showConfig

---generateTrace

$ tsc --generateTrace outDir
$ tsc --generateTrace outDir

Tool to analyze trace:

$ npx @typescript/analyze-trace ./outDir
$ npx @typescript/analyze-trace ./outDir

Improving

1. Check tsconfig — especially include/exclude settings

2. Name complex types

Examples:

3. Make your types/code simpler

4. Help TypeScript skip inference (if you really need to)

  • GraphQL Code Generator example

5. Be reasonable

So, my favourite thing — a traceResolution flag. It makes it easier to identify the parts of a program that are taking the most time to compile. It can tell you which of your files to examine more closely.

  • String literal templates example

Use —incremental flag

Still bad? Open an issue.

Example of a nice issue: https://github.com/microsoft/TypeScript/issues/53761