Reduce complexity, always sync
This commit is contained in:
Родитель
466551af20
Коммит
e356059661
|
@ -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 compiler’s 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 you’re 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 TypeScript’s 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 function’s return type). This is the heart of
|
||||
what makes generics useful—they can specify a _relationship_ between two types (e.g., a function’s output is the
|
||||
same as input, or a function’s two inputs are the same type). If a generic only uses its type parameter once, it
|
||||
doesn’t actually need to be generic at all, and indeed some linters will warn that it’s 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'
|
||||
```
|
||||
|
||||
It’s also possible to specify them explicitly, but it’s 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
|
Загрузка…
Ссылка в новой задаче