Reduce complexity, always sync

This commit is contained in:
Orta 2020-09-10 09:18:09 -04:00
Родитель 466551af20
Коммит e356059661
23 изменённых файлов: 22 добавлений и 1231 удалений

34
.github/workflows/sync.yml поставляемый
Просмотреть файл

@ -1,8 +1,16 @@
# Based on https://www.growingwiththeweb.com/2016/07/enabling-pull-requests-on-github-wikis.html
name: Sync Two Wiki Repos
on:
# Merged PRs
push:
branches: [master]
# Weekly
schedule:
# https://crontab.guru/#0_12_*_*_1
- cron: "0 12 * * 1"
# Whenever someone asks
workflow_dispatch:
jobs:
sync:
@ -14,19 +22,19 @@ jobs:
- run: git config user.name "typescript-bot"
- run: git config user.email "bot@typescriptlang.org"
# Add the authed remotes for .
- run: git remote add upstream https://$GITHUB_TOKEN@github.com/microsoft/TypeScript.wiki.git > /dev/null 2>&1
# Replace remotes with auth'd versions, then pull the .wiki version (the one people can't PR to)
# then merge in any changes from here and push
-
run: |
git remote add upstream https://$GITHUB_TOKEN@github.com/microsoft/TypeScript.wiki.git > /dev/null 2>&1
git remote remove origin
git remote add origin https://$GITHUB_TOKEN@github.com/microsoft/TypeScript-wiki.git > /dev/null 2>&1
git remote add upstream https://$GITHUB_TOKEN@github.com/microsoft/TypeScript.wiki.git > /dev/null 2>&1
git fetch origin
git fetch upstream
git merge upstream/master --no-edit
git push origin HEAD:master > /dev/null 2>&1
git push upstream HEAD:master > /dev/null 2>&1
env:
GITHUB_TOKEN: ${{ secrets.TS_BOT_TOKEN }}
# Update search links from the origin repo before pushing to the upstream
- run: git clone --depth 1 https://github.com/microsoft/TypeScript.git TypeScript
- run: npm i
- run: node scripts/convertRelativeLinksToHardcoded.js "**/*.md" --write
- run: rm -rf TypeScript
- run: git add .
- run: git commit --amend --no-edit
# Push to the auth'd branch
- run: git push upstream HEAD:master -f > /dev/null 2>&1

Просмотреть файл

@ -1,24 +1 @@
When a spec change is checked in (for example [#589](https://github.com/Microsoft/TypeScript/pull/589)), it is necessary to ensure thorough test coverage for all the areas that have changed. In other words, all changes in the spec should be observable somewhere in the tests/cases/conformance folder. The following process explains how to ensure this coverage.
For each spec change:
1. Try to find a bug for the spec change. Also look at any pull requests associated with that bug.
1. If there is a pull request that changed the compilers behavior, inspect the tests that were added as part of that pull request. If they look fitting, move them to the appropriate spot in the conformance suite. Update the baselines (the only change should be the path of the test they reference).
1. If there is no pull request or no bug, try to find a test in [`tests/cases/conformance`](https://github.com/Microsoft/TypeScript/tree/master/tests/cases/conformance/) that exercises this scenario. If there is, verify that the baseline agrees with the spec.
1. If there is no test in conformance, but there is a test in compiler that looks suitable, move it into conformance, and again verify that the baseline agrees with the spec.
1. If there is no existing test, or youre unable to find one, write a test in the conformance suite. Then verify that the baselines match the spec.
1. If in any of the steps above, the baseline did not match the spec, try to find a bug that says to change the compiler or to change the spec. Make sure that bug is up to date, and add the test info to the bug. If it is closed, reopen it.
1. If there is no bug, find out if the spec or the implementation should be changed, and log a bug for it.
When this process is complete, send a pull request for any tests you have added. Check it in as a normal code change (an example pull request is [#621](https://github.com/Microsoft/TypeScript/pull/621)). Make sure to reference the spec change pull request in your pull request.
Some guidelines and examples for specific test:
* Sometimes if there is a code change associated with a spec change, the tests that have been added along with the code change are sufficient, and merely need to be moved. An example is [`tests/cases/compiler/indexSignatureTypeInference.ts`](https://github.com/Microsoft/TypeScript/tree/master/tests/cases/compiler/indexSignatureTypeInference.ts) in pull request [#196](https://github.com/Microsoft/TypeScript/pull/196). You can see in pull request [#621](https://github.com/Microsoft/TypeScript/pull/621), this file was just moved into the conformance folder.
* Other changes are in broad areas that are very well tested already. For example, the change to best common type (section 3.10) in [#589](https://github.com/Microsoft/TypeScript/pull/589) has an entire folder under [`tests/cases/conformance/types/typeRelationships/bestCommonType`](https://github.com/Microsoft/TypeScript/tree/master/tests/cases/conformance/types/typeRelationships/bestCommonType). To verify that this is sufficient, make sure that every bullet in the changed spec has some code in the test folder that exercises it.
* Lastly, for new sections of the spec, and for sections where we have poor coverage, it may be necessary to add some amount of new test material. An example is the change to type queries (section 3.6.3) in change [#589](https://github.com/Microsoft/TypeScript/pull/589). Take a look at file [`tests/cases/conformance/types/specifyingTypes/typeQueries/recursiveTypesWithTypeof.ts`](https://github.com/Microsoft/TypeScript/blob/specConformanceTests/tests/cases/conformance/types/specifyingTypes/typeQueries/recursiveTypesWithTypeof.ts) in [#621](https://github.com/Microsoft/TypeScript/pull/621) to see an example of adding tests for a change like this. The amount of testing necessary here is based on judgment of how complex the changed area is.
## This page is deprecated

Просмотреть файл

@ -1,4 +1,3 @@
### Want to contribute to this Wiki?
[Fork it and send a pull request.](https://github.com/Microsoft/TypeScript-wiki)

Просмотреть файл

@ -1,270 +0,0 @@
### Terminology from inside the codebase
You can also see the [Data Structures](https://github.com/microsoft/TypeScript/wiki/Architectural-Overview#data-structures) section of the architecture overview.
- `Parser` - Takes source code and tries to convert it into an in-memory AST representation which you can work
with in the compiler. Also: [see Parser](https://basarat.gitbooks.io/typescript/docs/compiler/parser.html)
- `Scanner` - Used by the parser to convert a string an chops into tokens in a linear fashion, then it's up to a
parser to tree-ify them. Also: [see Scanner](https://basarat.gitbooks.io/typescript/docs/compiler/scanner.html)
- `Binder` - Creates a symbol map and uses the AST to provide the type system
[See Binder](https://basarat.gitbooks.io/typescript/docs/compiler/binder.htmlhttps://basarat.gitbooks.io/typescript/docs/compiler/binder.html)
- `Checker` - Takes the AST, symbols and does the type checking and inference -
[See Checker](https://basarat.gitbooks.io/typescript/docs/compiler/checker.html)
- `Token` - A set of characters with some kind of semantic meaning, a parser generates a set of tokens
- `AST` - An abstract syntax tree. Basically the in-memory representation of all the identifiers as a tree of
tokens.
- `Node` - An object that lives inside the tree
- `Location` / `Range`
- `Freshness` - When a literal type is first created and not expanded by hitting a mutable location, see [Widening
and Narrowing in TypeScript][wnn].
- `Symbol` - The binder creates symbols which connects declarations together
### Type stuff which can be see outside the compilers
- `Structural Type System` - A school of types system where the way types are compared is via the structure of
their properties.
For example:
```ts
interface Duck {
hasBeak: boolean;
flap: () => void;
}
interface Bird {
hasBeak: boolean;
flap: () => void;
}
```
These two are the exact same inside TypeScript. The basic rule for TypeScripts structural type system is that
`x` is compatible with `y` if `y` has at least the same members as `x`.
* `Literal` - A literal type is a type that only has a single value, e.g. `true`, `1`, `"abc"`, `undefined`.
For immutable objects, TypeScript creates a literal type which is is the value. For mutable objects TypeScript
uses the general type that the literal matches. See [#10676](https://github.com/Microsoft/TypeScript/pull/10676)
for a longer explanation.
```ts
// The types are the literal:
const c1 = 1; // Type 1
const c2 = c1; // Type 1
const c3 = "abc"; // Type "abc"
const c4 = true; // Type true
const c5 = c4 ? 1 : "abc"; // Type 1 | "abc"
// The types are the class of the literal, because let allows it to change
let v1 = 1; // Type number
let v2 = c2; // Type number
let v3 = c3; // Type string
let v4 = c4; // Type boolean
let v5 = c5; // Type number | string
```
- `Control Flow Analysis` - using the natural branching and execution path of code to change the types at
different locations in your source code by static analysis.
```ts
type Bird = { color: string, flaps: true };
type Tiger = { color: string, stripes: true };
declare animal: Bird | Tiger
if ("stripes" in animal) {
// Inside here animal is only a tiger, because TS could figure out that
// the only way you could get here is when animal is a tiger and not a bird
}
```
- `Generics` - A way to have variables inside a type system.
```ts
function first(array: any[]): any {
return array[0];
}
```
You want to be able to pass a variable type into this function, so you annotate the function with angle brackets
and a _type parameter_:
```ts
function first<T>(array: T[]): T {
return array[0];
}
```
This means the return type of `first` is the same as the type of the array elements passed in. (These can start
looking very complicated over time, but the principle is the same; it just looks more complicated because of the
single letter.) Generic functions should always use their type parameters in more than one position (e.g. above,
`T` is used both in the type of the `array` parameter and in the functions return type). This is the heart of
what makes generics useful—they can specify a _relationship_ between two types (e.g., a functions output is the
same as input, or a functions two inputs are the same type). If a generic only uses its type parameter once, it
doesnt actually need to be generic at all, and indeed some linters will warn that its a _useless generic_.
Type parameters can usually be inferred from function arguments when calling generics:
```ts
first([1, 2, 3]); // 'T' is inferred as 'number'
```
Its also possible to specify them explicitly, but its preferable to let inference work when possible:
```ts
first<string>(["a", "b", "c"]);
```
* `Outer type parameter` - A type parameter declared in a parent generic construct:
```ts
class Parent<T> {
method<U>(x: T, y: U): U {
// 'T' is an *outer* type parameter of 'method'
// 'U' is a *local* type parameter of 'method'
}
}
```
* `Narrowing` - Taking a union of types and reducing it to fewer options.
A great case is when using `--strictNullCheck` when using control flow analysis
```ts
// I have a dog here, or I don't
declare const myDog: Dog | undefined;
// Outside the if, myDog = Dog | undefined
if (dog) {
// Inside the if, myDog = Dog
// because the type union was narrowed via the if statement
dog.bark();
}
```
- `Expanding` - The opposite of narrowing, taking a type and converting it to have more potential values.
```ts
const helloWorld = "Hello World"; // Type; "Hello World"
let onboardingMessage = helloWorld; // Type: string
```
When the `helloWorld` constant was re-used in a mutable variable `onboardingMessage` the type which was set is an
expanded version of `"Hello World"` which went from one value ever, to any known string.
- `Transient` - unsure
- `Partial Type` -
- `Synthetic` -
- `Union Types`
- `Enum`
- `Discriminant`
- `Intersection`
- `Indexed Type` - A way to access subsets of your existing types.
```ts
interface User {
profile: {
name: string;
email: string;
bio: string;
};
account: {
id: string;
signedUpForMailingList: boolean;
};
}
type UserProfile = User["profile"]; // { name: string, email: string, bio: string }
type UserAccount = User["account"]; // { id: string, signedUpForMailingList: string }
```
This makes it easier to keep a single source of truth in your types.
- `Index Signatures` - A way to tell TypeScript that you might not know the keys, but you know the type of values
of an object.
```ts
interface MySettings {
[index: string]: boolean;
}
declare function getSettings(): MySettings;
const settings = getSettings();
const shouldAutoRotate = settings.allowRotation; // boolean
```
- `IndexedAccess` - ( https://github.com/Microsoft/TypeScript/pull/30769 )
- `Conditional Types`
- `Contextual Types`
- `Substitution`
- `NonPrimitive`
- `Instantiable`
- `Tuple` - A mathematical term for a finite ordered list. Like an array but with a known length.
TypeScript lets you use these as convenient containers with known types.
```ts
// Any item has to say what it is, and whether it is done
type TodoListItem = [string, boolean];
const chores: TodoListItem[] = [["read a book", true], ["done dishes", true], ["take the dog out", false]];
```
Yes, you could use an object for each item in this example, but tuples are there when it fits your needs.
- `Mapped Type` - A type which works by taking an existing type and creating a new version with modifications.
```ts
type Readonly<T> = { readonly [P in keyof T]: T[P] };
// Map for every key in T to be a readonly version of it
// e.g.
interface Dog {
furColor: string;
hasCollar: boolean;
}
// Using this
type ReadOnlyDog = Readonly<Dog>;
// Would be
interface ReadonlyDog {
readonly furColor: string;
readonly hasCollar: boolean;
}
```
This can work where you
- `Type Assertion` - override its inferred and analyzed view of a type
```ts
interface Foo {
bar: number;
bas: string;
}
var foo = {} as Foo;
foo.bar = 123;
foo.bas = "hello";
```
- `Incremental Parsing` - Having an editor pass a range of edits, and using that range to invalidate a cache of
the AST. Re-running the type checker will keep the out of range nodes and only parse the new section.
- `Incremental Compiling` - The compiler keeps track of all compilation hashes, and timestamps when a file has
been transpiled. Then when a new module is changed, it will use the import/export dependency graph to invalidate
and re-compile only the affected code.
### Rarely heard
- `Deferred` - A type might not be able to infer yet, because it is waiting for other types to be inferred first
- `Homomorphic` - A map between objects which means that the mapping applies to every property of the object
### JS Internals Specifics
[`Statement`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements) - "JavaScript
applications consist of statements with an appropriate syntax. A single statement may span multiple lines.
Multiple statements may occur on a single line if each statement is separated by a semicolon. This isn't a
keyword, but a group of keywords."
[wnn]: https://github.com/sandersn/manual/blob/master/Widening-and-Narrowing-in-Typescript.md

Просмотреть файл

@ -1,25 +0,0 @@
# Inference within the Checker
There are different routes into the inference engine depending on where the object needs be be inferred (params,
return values, signature etc) but the system for inferring starts with [`function inferTypes`][0].
This function takes a few params:
- `inferences: InferenceInfo[]` - a config object
- `originalSource: Type`
- `originalTarget: Type`
- `priority: InferencePriority = 0`
- `contravariant = false` (see [strict function types][1]))
### Instance var inference
```ts
const a = "Hello World";
```
<!-- prettier-ignore-start -->
[0]: <src/compiler/checker.ts - function inferTypes(>
[1]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#strict-function-types
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,202 +0,0 @@
# Checker
Ok, yeah, so it's a 30k LOC file. Why 30k lines in one file? Well there's a few main arguments:
- All of the checker is in one place.
- Save memory by making it a global, to quote a comment in the parser:
> ```
> // Implement the parser as a singleton module. We do this for perf reasons because creating parser instances
> // can actually be expensive enough to impact us on projects with many source files.
> ```
* Lots of these functions need to know a lot about each other, the top of the function `createTypeChecker` has a
set of variables which are global within all of these functions and are liberally accessed.
Switching to different files means probably making [god objects][god], and the checker needs to be extremely
fast. We want to avoid additional calls for ambient context. There are architectural patterns for this, but it's
better to assume good faith that they've been explored already (8 years down the line now.)
Anyway, better to get started somewhere. I [asked online](https://twitter.com/orta/status/1148335807780007939)
about how people would try to study a file like this and I think one of the best paths is by following a
particular story as a file gets checked.
## An entry-point
The likely entry point is via a Program. The program has a memoized typechecker created in
[`getDiagnosticsProducingTypeChecker`][0] which creates a type checker.
The initial start of type checking starts with [`getDiagnosticsWorker`][1], worker in this case isn't a threading
term I believe ( at least I can't find anything like that in the code ) - it is set up to listen for diagnostic
results (e.g. warns/fails) and then triggers [`checkSourceFileWorker`][2].
This function starts at the root `Node` of any TS/JS file node tree: `SourceFile`. It will then have to recurse
through all of the [AST][ast] nodes in it's tree.
It doesn't start with a single recursive function though, it starts by looking through the SourceFile's
[`statements`][4] and through each one of those to get all the nodes. For example:
```ts
// Statement 1
const hi = () => "Hello";
// Statement 2
console.log(hi());
```
Which looks a bit like:
```sh
SourceFile
statements:
- VariableStatement
- declarationList: VariableDeclarationList # (because any const cna have many declarations in a row... )
- variables: VariableStatement
- etc
- ExpressionStatement
- expression: CallExpression # outer console.log
- expression: PropertyAccessExpression
- etc
- arguments: CallExpression
- etc
```
[See AST Explorer](https://astexplorer.net/#/gist/80c981c87035a45a753c0ee5c983ecc9/6276351b153f4dac9811bf7214c9b236ae420c7e)
Each node has a different variable to work with (so you can't just say
`if node.children { node.children.foreach(lookAtNode) }` ) but instead you need to examine each node individually.
## Checking a Statement
Initially the meat of the work starts in [`checkSourceElementWorker`][6] which has a by `switch` statement that
contains all legitimate nodes which can start a statement. Each node in the tree then does it's checking.
Let's try get a really early error, with this bad code:
```ts
// A return statement shouldn't exist here in strict mode (or any mode?)
return;
~~~~~~
```
It goes into [`checkReturnStatement`][6] which uses [`getContainingFunction`][7] to determine if the `return`
lives inside a [`function-like`][8] node ( e.g. `MethodSignature`, `CallSignature`, `JSDocSignature`,
`ConstructSignature`, `IndexSignature`, `FunctionType`, `JSDocFunctionType`, `ConstructorType`).
Because the parent of the `return` statement is the root (`parent: SourceFileObject`) then it's going to fail.
This triggers [`grammarErrorOnFirstToken`][9] which will raise the error:
`A 'return' statement can only be used within a function body.ts(1108)` and declare the error underline to the
first token inside that node.
## Checking Type Equality
```ts
const myString = "Hello World";
const myInt = 123;
// Error: This condition will always return 'false' since the types '123' and '"Hello World"' have no overlap.
if (myInt === myString) {
// Do something
}
```
To get to this error message:
- [`checkSourceElementWorker`][6] loops through the 3 statements in the `SourceFile`
- In the third, it goes through:
- [`checkIfStatement`][13]
- [`checkTruthinessExpression`][11]
- [`checkExpression`][12]
- [`checkBinaryLikeExpression`][14] where it fails.
- The fail comes from inside [`isTypeRelatedTo`][15] which has a set of heuristics for whether the types are the
same ([`isSimpleTypeRelatedTo`][16] uses cached data in NodeLinks, and [`checkTypeRelatedTo`][17] dives deeper)
and if not then. It will raise.
### Caching Data While Checking
Note there are two ways in which TypeScript is used, as a server and as a one-off compiler. In a server, we want
to re-use as much as possible between API requests, and so the Node tree is treated as immutable data until there
is a new AST.
This gets tricky inside the Type Checker, which for speed reasons needs to cache data somewhere. The solution to
this is the [`NodeLinks`][3] property on a Node. The Type Checker fills this up during the run and re-uses it,
then it is discarded next time you re-run the checker.
### Type Flags
Because TypeScript is a [structural type system][20], every type can reasonably be compared with every other type.
One of the main ways in which TypeScript keeps track of the underlying data-model is via the
[`enum TypeFlags`][19]. Accessed via `.flags` on any node, it is a value which is used via bitmasking to let you
know what data it represents.
If you wanted to check for whether the type is a union:
```ts
if (target.flags & TypeFlags.Union && source.flags & TypeFlags.Object) {
// is a union object
}
```
When running the compiler in debug mode, you can see a string version of this via `target.__debugFlags`.
### Type Comparison
The entry point for comparing two types happens in [`checkTypeRelatedTo`][17]. This function is mostly about
handling the diagnostic results from any checking though and doesn't do the work. The honour of that goes to
[`isRelatedTo`][18] which:
- Figures out what the source and target types should be (based on freshness (a literal which was created in an
expression), whether it is substituted () or simplifiable (a type which depends first on resolving another
type))
- First, check if they're identical via [`isIdenticalTo`][21]. The check for most objects occurs in
[`recursiveTypeRelatedTo`][22], unions and intersections have a check that compares each value in
[`eachTypeRelatedToSomeType`][23] which eventually calls [`recursiveTypeRelatedTo`][22] for each item in the
type.
- The heavy lifting of the comparison depending on what flags are set on the node is done in
[`structuredTypeRelatedTo`][23]. Where it picks off one by one different possible combinations for matching and
returns early as soon as possible.
A lot of the functions related to type checking return a [`Ternary`][24], which an enum with three states, true,
false and maybe. This gives the checker the chance to admit that it probably can't figure out whether a match is
true currently (maybe it hit the 100 depth limit for example) and potentially could be figured out coming in from
a different resolution.
## Debugging Advice
- If you want to find a diagnostic in the codebase, search for `diag(WXZY` e.g ``, you'll find
`src/compiler/diagnosticInformationMap.generated.ts` has it being referenced by a key. Search for that key.
- If you're working from an error and want to see the path it took to get there, you can add a breakpoint in
[`createFileDiagnostic`][10] which should get called for all diagnostic errors.
<!-- prettier-ignore-start -->
[0]: <src/compiler/program.ts - function getDiagnosticsProducingTypeChecker>
[1]: <src/compiler/checker.ts - function getDiagnosticsWorker>
[2]: <src/compiler/checker.ts - function checkSourceFileWorker>
[3]: </src/compiler/types.ts - export interface NodeLinks>
[4]: GLOSSARY.md#statements
[ast]: GLOSSARY.md#statements
[5]: <src/compiler/checker.ts - function checkSourceElementWorker>
[6]: <src/compiler/checker.ts - function checkReturnStatement>
[7]: <src/compiler/checker.ts - export function getContainingFunction>
[8]: <src/compiler/utilities.ts - export function isFunctionLikeKind>
[9]: <src/compiler/checker.ts - function grammarErrorOnFirstToken>
[10]: <src/compiler/utilities.ts - export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): DiagnosticWithLocation>
[11]: <src/compiler/checker.ts - function checkTruthinessExpression>
[12]: <src/compiler/checker.ts - function checkExpression>
[13]: <src/compiler/checker.ts - function checkIfStatement>
[14]: <src/compiler/checker.ts - function checkBinaryLikeExpression>
[15]: <src/compiler/checker.ts - function isTypeRelatedTo>
[16]: <src/compiler/checker.ts - function isSimpleTypeRelatedTo>
[17]: <src/compiler/checker.ts - function checkTypeRelatedTo>
[17]: <src/compiler/checker.ts - function isRelatedTo>
[19]: <src/compiler/types.ts - export const enum TypeFlags>
[20]: GLOSSARY.md#structural-type-system
[21]: <src/compiler/checker.ts - function isIdenticalTo>
[22]: <src/compiler/checker.ts - function recursiveTypeRelatedTo>
[22]: <src/compiler/checker.ts - function eachTypeRelatedToSomeType>
[23]: <src/compiler/checker.ts - function structuredTypeRelatedTo>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,47 +0,0 @@
## Emitter
The emitter is a tree based syntax emitter. It works by going through the TypeScript AST for a program and
emitting source code as it is pipelined.
The emitter itself is "dumb" in the sense that it doesn't contain logic outside of printing whatever AST it is
given. So, it's possible that a bug in emission is actually that the AST isn't set up the way that you'd like it.
### Outfile
Creating a single file which represents many is done by creating a `SyntaxKind.Bundle`. Printing happens in
[`function writeBundle(`][0]. There are `prepends` which I don't understand, and then each sourcefile is is
printed.
### Printer
The printer is a part of the emitter, you create one with [`createPrinter`][1], then start calling [`print`][2]
with an AST node on it. This adds the node via a [pipeline][3]:
```ts
const enum PipelinePhase {
Notification,
Substitution,
Comments,
SourceMaps,
Emit
}
```
With the word to start emitting through the AST in [`pipelineEmitWithHint`][4]. There is a hint option which can
be used to force the emit type.
## Post Processing via Transformers
The process of changing your AST into the expected JS or TS happens through compiler transformers. There is a full
step
Emitting a declaration file is a multi-step process. It goes through the above emitter of its AST, but then _also_
goes through a
<!-- prettier-ignore-start -->
[0]: <src/compiler/emitter.ts - function writeBundle>
[1]: <src/compiler/emitter.ts - function createPrinter>
[2]: <src/compiler/emitter.ts - function print(>
[3]: <src/compiler/emitter.ts - const enum PipelinePhase>
[3]: <src/compiler/emitter.ts - function pipelineEmitWithHint(>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,20 +0,0 @@
# Parser
At a measly 8k lines long, the Parser is responsible for controlling a scanner (or two) and turning the output
tokens from the scanner into an AST as the canonical representation of the source file.
## JSDoc
## Context
Because the parser itself is effectively a state machine which creates nodes from scanning text there is some
reasonable dancing
<!-- prettier-ignore-start -->
[0]: <src/compiler/program.ts - function getDiagnosticsProducingTypeChecker>
[4]: GLOSSARY.md#statements
<!-- prettier-ignore-end -->
`

Просмотреть файл

@ -1,92 +0,0 @@
# Scanner
One of the smallest parts of the compiler's critical path to AST then type-checking. It exists to create a stream
of syntax tokens for use by another object. The scanner gets most of its usage via a [parser][0] instance.
## Overview
You create a Scanner instance via [`createScanner`][1], there are two main modes of scanning: Standard and JSX.
Then there are specific functions for scanning inside JSDoc comments.
First you set the text of a scanner to be your JS source code via [`setText`][2], this takes the source code and
an optional start and end range in which you want to scan. This range is an important part of what keeps
TypeScript's server mode fast via [incremental parsing][3].
At a high level, the scanner works by a having another object call [`scanner.scan`][4] this:
- Pulls out the character at the current position (`pos`) in the text
- Runs a very big switch statement against known characters which start syntax
- Then move look forwards till the end of that list of chars to decode if it's what we think it is. here's an
example of what happens when [the character found][5] is a `!`:
```ts
case CharacterCodes.exclamation:
// Look to see if it's "!="
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
// Also check if it's "!=="
if (text.charCodeAt(pos + 2) === CharacterCodes.equals) {
// for !== move the position forwards to the end of the symbol
// then set the token
return pos += 3, token = SyntaxKind.ExclamationEqualsEqualsToken;
}
// Move it two, set the token
return pos += 2, token = SyntaxKind.ExclamationEqualsToken;
}
// Move forwards one character and set the token
pos++;
return token = SyntaxKind.ExclamationToken;
```
- A `SyntaxKind` is returned from [`scan`][4] and it's up to the scanner owner to do work with those tokens. The
scanner keeps track of a few useful values for that:
- `getStartPos` - where the token was started including preceding whitespace
- `getTextPos` - the end position of the current token
- `getTokenText` - the text between the token start and end
- `getTokenValue` - some syntax contains a value which can be represented as a string, a good example is
literally a string. The `"` or `'` are not included in the value.
## Trivia
When creating a scanner you get to choose whether whitespace should be returned in the stream of tokens. This is
nearly always off, but it is used inside the [formatter][6] and for syntax highlighting via the TSServer via a
[classifier][7].
## JSX
Some of the more complicated aspects of JSX support is mostly handled back in [the parser][0], however JSX support
in the scanner [uses specific syntax tokens][8].
## Flags
One way for the scanner to keep track of scan issues, or internal state is [via `TokenFlags`][9]. Any example of
this is in scanning a number. TypeScript supports underscores in numbers `100_000`, when scanning a number literal
if it detects a `CharacterCodes._` then the flag `TokenFlags.ContainsSeparator` is set and later on that is used
to ensure the `tokenValue` is set correctly.
## Rescanning
Because the scanner is only interested in passing out tokens as it sees them, it doesn't really have a memory of
previous tokens. This means that occasionally the controlling object will need to rewind and re-run the scanner
with a different type of context. This is called rescanning.
## Example code
[Here's a scanner playground](https://5d39df23407c626e65aee7ef--ts-scanner-tokens.netlify.com) - adding TypeScript
will show you the tokens generated by a single scanner. It's worth noting that this doesn't represent that
_actual_ results of the scanner when using TypeScript, because the parser controls re-scanning and this playground
doesn't do that.
<!-- prettier-ignore-start -->
[0]: ./parser.md
[1]: <src/compiler/scanner.ts - export function createScanner>
[2]: <src/compiler/scanner.ts - function setText(>
[3]: GLOSSARY.md#incremental-parsing
[4]: <src/compiler/scanner.ts - function scan(>
[5]: <src/compiler/scanner.ts - case CharacterCodes.exclamation>
[6]: ./formatter.md
[7]: <src/services/classifier.ts - function createClassifier>
[8]: <src/compiler/types.ts - type JsxTokenSyntaxKind>
[9]: <src/compiler/types.ts - export const enum TokenFlags>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,13 +0,0 @@
# Type Hierarchy
Root class: `Type`
### How are properties stored and found on a type?
`checkPropertyAccessExpressionOrQualifiedName`
<!-- prettier-ignore-start -->
[1]: <src/compiler/checker.ts - function checkPropertyAccessExpressionOrQualifiedName(
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,13 +0,0 @@
### Util Functions
Some essentials:
- [`findAncestor`][0]
> Iterates through the parent chain of a node and performs the callback on each parent until the callback returns
> a truthy value, then returns that value.
>
> If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns
> "quit" At that point findAncestor returns undefined.
Basically looks up the AST until it finds something which passes.

Просмотреть файл

@ -1,39 +0,0 @@
This page follows on from [[Learn the Compiler|learn-the-compiler]].
### Getting set up
To get your VS Code env working smoothly, set up the per-user config
- Set up your `.vscode/launch.json` by running: `cp ./vscode/launch.template.json ./vscode/launch.json`
- Set up your `.vscode/settings.json` by running: `cp ./vscode/settings.template.json ./vscode/settings.json`
In the `launch.json` I duplicate the configuration, and change `"${fileBasenameNoExtension}",` to be whatever test
file I am currently working on.
### Learn the debugger
You'll probably spend a good amount of time in it, if this is completely new to you. Here is a video from
[@alloy](https://github.com/alloy) covering all of the usage of the debugger inside VS Code and how it works.
To test it out, open up `src/compiler/checker.ts` find `function checkSourceFileWorker(node: SourceFile) {` and
add a debugger on the first line in the code. If you open up `tests/cases/fourslash/getDeclarationDiagnostics.ts`
and then run your new debugging launch task `Mocha Tests (currently opened test)`. It will switch into the
debugger and hit a breakpoint in your TypeScript.
You'll probably want to add the following to your watch section in VS Code:
- `node.__debugKind`
- `node.__debugGetText()`
- `source.symbol.declarations[0].__debugKind`
- `target.symbol.declarations[0].__debugKind`
This is really useful for keeping track of changing state, and it's pretty often that those are the names of
things you're looking for.
If you want to see what a debugging session looks like with the compiler team, watch [these](https://www.youtube.com/watch?v=3vwO4DwlGYE) [videos](https://www.youtube.com/watch?v=Xq4p5LXaO8Y).
### Getting started
You can learn about the different ways to [[write a test here|systems-testing]] to try and re-produce a bug. It
might be a good idea to look in the recently closed PRs to find something small which adds a test case, then read
the test and the code changes to get used to the flow.

Просмотреть файл

@ -1,41 +0,0 @@
### Getting Started with the Compiler
Yep, the compiler can be a bit of an overwhelming project to understand from scratch, however there are some great
places to start learning.
You should run this in the background while you explore the wiki:
```sh
# clone
git clone https://github.com/microsoft/TypeScript
cd TypeScript
# setup dependencies and build
npm i
gulp
# open up your editor
code .
# run tests to validate your install
gulp runtests-parallel
```
To dive deeper into getting set up to work on the codebase, read: [[Getting Set Up|getting-started-with-the-compiler]].
### Critical prior knowledge
1. Read the [Architecture Document](https://github.com/microsoft/TypeScript/wiki/Architectural-Overview) to understand the pieces
1. Read the [Compiler API Examples](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API) to see some small scoped examples of the compiler in action
### Useful to know exists
1. [A glossary of terms](./GLOSSARY.md)
1. The [codebase](./codebase) and [systems](./systems) folders contain overviews of different parts of the compiler
1. The [section on testing](./systems/testing)
### Talks and other content
- [Basarat's Compiler Internals](https://basarat.gitbooks.io/typescript/content/docs/compiler/overview.html)
- The TypeScript compiler team review JSDoc Pull Requests: [part 1](https://www.youtube.com/watch?v=3vwO4DwlGYE), [part 2](https://www.youtube.com/watch?v=Xq4p5LXaO8Y)

Просмотреть файл

@ -1,38 +0,0 @@
## Completions in TS
Completions come in through the language service, and most of the work starts in
[`getCompletionEntriesFromSymbols`](https://github.com/Microsoft/TypeScript/blob/340f81035ff1d753e6a1f0fedc2323d169c86cc6/src/services/completions.ts#L305).
### Completion Data
Step one is to grab [a `CompletionData`][1] via [`getCompletionData`][2]. This function tries to find a context
token which first looks forwards, and then try find a `contextToken`. This is generally the preceding token to
your cursor, as that tends to be the most important thing when deciding what to show next. This takes into account
things like `x.y` and `y?.y` by diving deeper into preceding identifier.
This dive to find a "responsible" item for a completion request called `node` in the code.
Next it goes through the following checks for a set of completions.
#### String Literal Completions
E.g. are you inside a string and asking for completions? TS differentiates between reference comments
([triple slash](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html)):
![./screenshots/threeslash-refs.png](./screenshots/threeslash-refs.png)
And strings as a part of the AST. These have a
[few](https://github.com/Microsoft/TypeScript/blob/340f81035ff1d753e6a1f0fedc2323d169c86cc6/src/services/stringCompletions.ts#L103)
different uses:
- They could be path references
- They could be module references
- They could be indexed keys from an object
- They could be parts of a union object
####
<!-- prettier-ignore-start -->
[1]: <src/services/completions.ts - interface CompletionData {>
[2]: <src/services/completions.ts - function getCompletionData(>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,67 +0,0 @@
# Text Changes
The majority of this file is devoted to a class called the [`ChangeTracker`][0]. This class is nearly always
created via `ChangeTracker.with` where you would give it a context object.
Here is an example context object:
```ts
{
cancellationToken:CancellationTokenObject {cancellationToken: TestCancellationToken}
errorCode:2304
formatContext:Object {options: Object, getRule: }
host:NativeLanguageServiceHost {cancellationToken: TestCancellationToken, settings: Object, sys: System, …}
preferences:Object {}
program:Object {getRootFileNames: , getSourceFile: , getSourceFileByPath: , …}
sourceFile:SourceFileObject {pos: 0, end: 7, flags: 65536, …}
span:Object {start: 0, length: 6}
}
```
You only really see `ChangeTrack` in use within the codefixes and refactors given that the other case where
TypeScript emits files is a single operation of emission.
The change tracker keeps track of individual changes to be applied to a file. There are [currently][1] four main
APIs that it works with:
`type Change = ReplaceWithSingleNode | ReplaceWithMultipleNodes | RemoveNode | ChangeText;`
The `ChangeTrack` class is then used to provide high level API to describe the sort of changes you might want to
make, which eventually fall into one of the four categories above.
### Making Changes
The end result of using a `ChangeTrack` object is an array of `FileTextChanges` objects. The `ChangeTrack.with`
function lets you work with a tracker instance elsewhere and passes back the `ChangeTrack` objects.
The core work in generating changes occurs in:
- [`getTextChangesFromChanges`][4]
- [`computeNewText`][5]
- [`getFormattedTextOfNode`][6]
Going from an AST node to text is done by creating a [`printer`][7] in [`getNonformattedText`][8]. The printer
returns an unformatted node, which is then ran through [a formatter][./formatting.md] and the raw string
substitution is done in [`applyChanges`][9].
Changes look like this:
```ts
[{ fileName: "/b.js", textChanges: [{ span: { start: 0, length: 0 }, newText: "// @ts-ignore\n" }] }];
```
### Writing
[`newFileChanges`][3] handles passing the set of
<!-- prettier-ignore-start -->
[0]: <src/services/textChanges.ts - export class ChangeTracker>
[1]: <src/services/textChanges.ts - type Change =>
[2]: <src/services/textChanges.ts - function createWriter>
[3]: <src/services/textChanges.ts - function newFileChanges>
[4]: <src/services/textChanges.ts - function getTextChangesFromChanges>
[5]: <src/services/textChanges.ts - function computeNewText>
[6]: <src/services/textChanges.ts - function getFormattedTextOfNode>
[7]: <src/compiler/emitter.ts - function createPrinter>
[8]: <src/services/textChanges.ts - function getNonformattedText>
[8]: <src/services/textChanges.ts - function applyChanges>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,37 +0,0 @@
# Baselines
Baseline tests are effectively large folder-based snapshot tests. Like [[Baselines|systems-fourslash]], these tests are
somewhat integration level. These test generally test the internals of the compiler, they do this by you creating
a TypeScript file where the snapshot result is a symbol map of all the symbols in that file and a list of all
raised errors then compiling the tests.
### How to make a baseline
1. Make a new file: ` touch tests/cases/compiler/overloadFunctionsNotEqualToGenerics.ts`.
1. Add some TypeScript to it.
1. Run run your test in
### How to amend a baseline
`gulp runtests` will run all the baselines eventually. Or `gulp runtests -i --tests=baseline` should speed things up
if you only want to see those specific changes.
All of the baselines exist in `tests/baselines/local/`. They kinda look like:
```sh
$ tree tests/baselines/local/
tests/baselines/local/
└── api
├── tsserverlibrary.d.ts
└── typescript.d.ts
1 directory, 2 files
```
This lets you know that two tests have changed from what you expect. They live in `local/api`, so you can compare
the folders. The team loves [Beyond Compare](https://scootersoftware.com) - it does a good job showing you how the
folder infrastructure differs.
Once you're happy with the new baselines, you can run `gulp baseline-accept` to move them into the codebase.
You will be able to see your diffs in git now. :+1:

Просмотреть файл

@ -1,24 +0,0 @@
## CLI
Oddly, the TS Config is probably best described by the CLI which you can find in
`src/compiler/commandLineParser.ts`.
The CLI starts after parsing [`tsc.ts`][0] which triggers [`executeCommandLine`][1]. This function handles going
from an arg list to structured data in the form of [ParsedCommandLine][2].
The actual work occurs in [`parseCommandLineWorker`][] which sets up some variables:
```ts
const options = {} as OptionsBase;
const fileNames: string[] = [];
const errors: Diagnostic[] = [];
```
Then it starts If the letter is a - then start looking forwards to see if the arg is available in [the
optionmap][4]
[0]: https://github.com/microsoft/TypeScript/blob/master/src/tsc/tsc.ts
[1]: <src/tsc/tsc.ts - function executeCommandLine>
[2]: <src/compiler/types.ts - export interface ParsedCommandLine>
[3]: <src/compiler/commandLineParser.ts - function parseCommandLineWorker>
[4]: <src/compiler/commandLineParser.ts - export const optionDeclarations>

Просмотреть файл

@ -1,100 +0,0 @@
### How do Codefixes work
A codefix is a signal from TypeScript to an editor that it's possible for TypeScript to provide an automated fix
for some particular part of a codebase.
Codefixes are implemented in a way that always requires there to be an error code, which means they bubble through
TypeScript's internals initially from a compiler error.
### Codefix vs Refactor
Code fixes have an associated error code, and can be skipped cheaply when trying to figure out if it's applicable.
A refactor on the other hand does not come from an error code and is therefore somewhat always available, and are
more expensive to check for.
### How are they used?
The code fix assertions come in from comes in from the language service, via [`getCodeFixesAtPosition`][1], this
says here's a file and a selection range for their selected text and any potential compiler error codes that touch
section.
The language service then reaches into [the codeFixProvider][2] via [`getFixes`][3], this delegates its work to
[`getCodeActions`][4] which is a function which each codefix provides.
These are returned to the IDE in the form of an object with:
```ts
interface CodeAction {
/** Description of the code action to display in the UI of the editor */
description: string;
/** Text changes to apply to each file as part of the code action */
changes: FileTextChanges[];
/**
* If the user accepts the code fix, the editor should send the action back in a `applyAction` request.
* This allows the language service to have side effects (e.g. installing dependencies) upon a code fix.
*/
commands?: CodeActionCommand[];
/** Short name to identify the fix, for use by telemetry. */
fixName: string;
/**
* If present, one may call 'getCombinedCodeFix' with this fixId.
* This may be omitted to indicate that the code fix can't be applied in a group.
*/
fixId?: {};
fixAllDescription?: string;
}
```
An example of one is:
```ts
const codefix = {
description: `Import 'moment' from module "moment"`,
fixAllDescription: "Add all missing imports",
fixId: "fixMissingImport",
fixName: "import"
};
```
### Testing a code fix
You can use fourslash to set up a project that emulates the sort of before and after experience from your codefix
```ts
/// <reference path="fourslash.ts" />
// #29038
// @allowJs: true
// @checkJs: true
// @esModuleInterop: true
// @moduleResolution: node
// @Filename: /node_modules/moment.d.ts
////declare function moment(): void;
////export = moment;
// @Filename: /b.js
////[|moment;|]
goTo.file("/b.js");
verify.importFixAtPosition([
`import moment from "moment";
moment;`
]);
```
### Example codefix PRs
These are some reference PRs to look at
- https://github.com/microsoft/TypeScript/pull/23711
- https://github.com/microsoft/TypeScript/pull/32281
<!-- prettier-ignore-start -->
[1]: <src/services/services.ts - function getCodeFixesAtPosition>
[2]: src/services/codeFixProvider.ts
[3]: <src/services/services.ts - export function getFixes>
[4]: <src/services/codeFixProvider.ts - export function getFixes>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,38 +0,0 @@
# How does TypeScript formatting work?
To format code you need to have a formatting context and a `SourceFile`. The formatting context contains all user
settings like tab size, newline character, etc.
The end result of formatting is represented by TextChange objects which hold the new string content, and the text
to replace it with.
```ts
export interface TextChange {
span: TextSpan; // start, length
newText: string;
}
```
## Internals
Most of the exposed APIs internally are `format*` and they all set up and configure `formatSpan` which could be
considered the root call for formatting. Span in this case refers to the range of the sourcefile which should be
formatted.
The formatSpan then uses a scanner (either with or without JSX support) which starts at the highest node the
covers the span of text and recurses down through the node's children.
As it recurses, `processNode` is called on the children setting the indentation is decided and passed through into
each of that node's children.
The meat of formatting decisions is made via `processPair`, the pair here being the current node and the previous
node. `processPair` which mutates the formatting context to represent the current place in the scanner and
requests a set of rules which can be applied to the items via `createRulesMap`.
There are a lot of rules, which you can find in [rules.ts](./rules.ts) each one has a left and right reference to
nodes or token ranges and note of what action should be applied by the formatter.
### Where is this used?
The formatter is used mainly from any language service operation that inserts or modifies code. The formatter is
not exported publicly, and so all usage can only come through the language server.

Просмотреть файл

@ -1,63 +0,0 @@
# Fourslash
The fourslash tests are an integration level testing suite. By this point they have a very large API surface, and
tend to cover a lot of the "user-facing" aspects of TypeScript. E.g. things which an IDE might have an interest in
knowing.
### How to run one
`gulp runtests` will run all the fourslash tests eventually. Or `gulp runtests -i --tests=[filename]` should speed
things up if you only want to see those specific changes.
### How Fourslash runs a test
Fourslash automatically generates mocha tests based on files you put inside [`/tests/cases/fourslash`][0] the code
for this lives in [`/src/testRunner/fourslashRunner.ts`][1]. This class is instantiated in
[`/src/testRunner/runner.ts`][2].
Fom here the main work all lives in [`/src/harness/fourslash.ts`][3] where we'll be spending the rest of this
section. The initial entry point is [`runFourSlashTest`][4] but the work is in [`runFourSlashTestContent`][5].
This function first creates a virtual fs, uses [`parseTestData`][6] to fill up the virtual fs. `parseTestData`:
- Loops through every line in the test file
- If the line starts with `////` then it starts piping the text into a new string which represents the current
file
- If the line starts with `//` then check whether it's a special case variable (like `Filename`) - if it's not
then it will get passed as though it were a TSConfig setting.
This isn't `eval`-ing the code, so the tests under are ignored. Here's an example test file:
```ts
/// <reference path="fourslash.ts" />
// #29038
// @allowJs: true
// @checkJs: true
// @Filename: /node_modules/moment.d.ts
////declare function moment(): void;
////export = moment;
// @Filename: /b.js
////[|moment;|]
goTo.file("/b.js");
verify.importFixAtPosition([
`import moment from "moment";
moment;`
]);
```
### Formatting
<!-- prettier-ignore-start -->
[0]: https://github.com/microsoft/TypeScript/blob/master/src/testRunner/fourslashRunner.ts
[1]: https://github.com/microsoft/TypeScript/blob/master/tests/cases/fourslash
[2]: https://github.com/microsoft/TypeScript/blob/master/src/testRunner/runner.ts
[3]: https://github.com/microsoft/TypeScript/blob/master/src/harness/fourslashImpl.ts
[4]: <src/harness/fourslashImpl.ts - function runFourSlashTest(>
[5]: <src/harness/fourslashImpl.ts - function runFourSlashTestContent(>
[5]: <src/harness/fourslashImpl.ts - function parseTestData(>
<!-- prettier-ignore-end -->

Просмотреть файл

@ -1,26 +0,0 @@
## For `console.log` debugging
A lot of the time to see what the code you're representing you want to look in either `declaration` or
`declarations`. These are Node subclasses which means they'll have a copy of `__debugGetText()` which should get
you to the syntax representation.
### An XXYYFlags
[Run](https://twitter.com/atcb/status/1174747774761324544) `ts.Debug.format[Whatever]Flags(flag)` in the console
to find out the constituent parts
### `Type`
Useful: `console.log(type.__debugKind)`.
Chances are through you want to go through symbol though
### `Symbol`
Using `symbolToString(symbol)` could get you somewhere, but so far that's not been
### `Node`
### `Signature`
`signature.declaration.__debugGetText()`

Просмотреть файл

@ -1,21 +0,0 @@
### Tests for TypeScript
Yep, TypeScript has tests. Quite a lot, with a few different techniques:
- [[Baselines|systems-baselines]]
- [[Fourslash|systems-fourslash]]
## Commands worth knowing
You run tests via `gulp runtests`.
Flags worth knowing:
- `--failed` - re-runs the failed tests only
- `--no-lint` - don't run the linter when it completes
- `-i` - Use the inspector to debug
If you have a fail in the baselines:
- `gulp diff` - to see the differences
- `gulp baseline-accept` to overwrite the current baseline snapshots

Просмотреть файл

@ -1,17 +0,0 @@
### VS Code + TypeScript
VS Code has three extensions related to TypeScript:
- `extensions/typescript` - this is an `npm install`'d version of TypeScript.
- `extensions/typescript-basics` - this is a extension which provides
[language grammars and defines the language of TypeScript](https://github.com/microsoft/vscode/commit/e23c58b3aba76f25bb99400619d39f285eeec9e1#diff-cdbcc33fea0f5bd15137cf1750d69776)
inside VS Code
- `extensions/typescript-language-features` - this extends the TypeScript language support with commands,
auto-complete et al.
### [`typescript-language-features`](https://github.com/microsoft/vscode/tree/master/extensions/typescript-language-features)
A large amount of the work happens in the [`TypeScriptServiceClient`][1] which is the VS Code side of the
TSServer.
[1]: https://github.com/microsoft/vscode/blob/master/extensions/typescript-language-features/src/typescriptServiceClient.ts#L75