This document goes over how TypeStart runs and generates fixes. Before reading this, you should read:
- TypeStat's README.md
- Documentation on the types of fixes
- Recommended TypeStat usage
- automutate and automutate-tests
When the typestat
command is entered into the CLI, roughly the following happens in order:
- bin/typestat calls to the CLI
- Commander.js parses the CLI's arguments
- Settings are loaded from the
-c
/--config
file - An Automutator provider is created for TypeStat with
createTypeStatMutationsProvider
.
There are three mutation providers that are run in order by createTypeStatMutationsProvider
:
- Require renames: changes to
import
andrequire
statements fromfiles.renameExtensions
- Core mutations: changes to type annotations in provided files
- Files modified: adds annotations to the top (
files.above
) and/or bottom (files.below
) of mutated files if enabled
If any require
to a file including the extension is stored as a variable, and files.renameExtensions
is enabled,
that variable will be given a type equivalent to the extensionless equivalent.
This is done as a separate mutation provider before the core mutations to ensure these mutations are applied before core mutations.
import
declarations cannot be given different types, so they are ignored.
Each round of mutations in the core mutation provider roughly:
- Records the starting time
- Creates a set of TypeScript language services
- For each file to be visited:
- Retrieves file mutations for that file
- If more than 100 mutations have been collected, or mutations have been collected and it's been more than 10 seconds since the round started, stops the round
Subsequent rounds will pick up where the previous round left off.
For example, given files a.ts
, b.ts
, and c.ts
in order,
if the first round runs on a.ts
and b.ts
, the second will start on c.ts
.
Rounds stop after those thresholds to allow Automutate to write mutations regularly. TypeStat crashing before a round is complete shouldn't lose all accumulated mutations.
Once TypeStat has visited each file, it will either:
- Stop if no file had mutations applied
- Restart (and reload language services) if any file had mutations applied
For each file it visits, findMutationsInFile
will attempt to apply built-in file mutators:
fixIncompleteTypes
fixMissingProperties
fixNoImplicitAny
fixNoImplicitThis
fixStrictNonNullAssertions
Each fixer targets a general range of potential type improvements and contains a series of sub-fixers that target individual improvements.
For example, fixIncompleteTypes
contains a fixIncompleteParameterTypes
fixer that fills in incomplete types for parameters.
Within each round of applying mutations, TypeStat will stop looking at a file after each step if any mutations are found. Adding mutations from one from can improve mutations from other forms, so reloading the file between rounds could reduce the number of later rounds.
See Fixes.md.
The following directories exist under src/
:
Definition and default implementation of the logger for runtime.
It wraps process.stderr
and process.stdout
and is given to the runtime mutators as a member of options
.
During tests, it'll be stubbed out.
A file for each of the forms of mutation run in a round.
These are stored in the above fastest-first order in the exported builtInFileMutators
array.
Called by the mutators
to create mutations.
The mutators
directory figures out which types should be modified, then mutations
creates mutations to modify them.
We should note two common pieces of terminology used in this directory:
- "Flags" refers to type nodes for primitive types, such as
boolean
- "Types" refers to rich (non-primitive) types, such as
MyClass
Parsing logic and TypeScript types for raw and parsed options.
loadOptions
will require
a -c
/--config
file from the path provided.
Options parsed from that file will be of type RawTypeStatOptions
,
and will be filled out into TypeStatOptions
via fillOutRawOptions
.
Automutate hooks that launch and run the processes described above. Think of this area as the coordinating force behind mutators.
Creates wrappers around the TypeScript Compiler API later used by mutators. These include the TypeScript Language Service API.
Language services are passed to mutators in the form of a LanguageServices
object.
They're recreated each mutations wave, as the underlying TypeScript source files generally change between wave.
Miscellaneous utilities used by other sections.
Why Not jscodeshift?
When I started on TypeStat, jscodeshift didn't yet support TypeScript. Now it does. Maybe this should be converted?
See #20.