Skip to content

Compiler glossary

AllenLee edited this page May 19, 2020 · 1 revision

Terminology from inside the codebase

原文 You can also see the 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 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 Binder - Creates a symbol map and uses the AST to provide the type system See Binder Checker - Takes the AST, symbols and does the type checking and inference - See Checker 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. Symbol - The binder creates symbols which connects declarations together

你可以在Overview处看到更多的细节.

  • Parser - 获取源代码并尝试将其转换为内存中(in-memory)的AST, 可以在compiler中使用
  • Scanner - 由parser所使用, 以线性方式(while(true))将字符串和符号(chops)转换为Token, 然后由parser解析为token树(tree-ify them)
  • Binder - 创建符号映射并使用AST构建(provide)类型系统
  • Checker - 接受AST及symbols并进行类型检查和推断
  • Token - 一组具有某种语义意义的字符, 由parser生成
  • AST - 抽象语法树. 基本上, 所有标识符的内存表示形式(in-memory representation)都是token树
  • Node - token树内的对象
  • Location / Range
  • Freshness - 当一个字面量类型被首次创建, 并且没有通过触发一个可变的位置(hitting a mutable location)进行扩展时, 参见Widening and Narrowing in Typescript
  • Symbol - 由binder创建, 用于将声明连接在一起(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: 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 for a longer explanation. // 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. 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. 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: function first(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: 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: first(["a", "b", "c"]); Outer type parameter - A type parameter declared in a parent generic construct: class Parent { method(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 // 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. 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. 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. 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. // 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. type Readonly = { 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; // 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 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.
  • 结构化类型系统(Structural Type System) - 通过类型的属性结构来比较类型
interface Duck {
  hasBeak: boolean;
  flap: () => void;
}

interface Bird {
  hasBeak: boolean;
  flap: () => void;
}

这两个在TypeScript中是完全相同的. TypeScript中结构化类型系统的基本规则是: 如果y至少有与x相同的成员, 则x与y兼容.

  • 字面量(Literal) - 字面量类型是仅具有单个值的类型,例如: true, 1, "abc", undefined.
// 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

对于不可变(immutable)对象, TypeScript会创建一个字面量类型,也就是值. 对于可变(mutable)对象, TypeScript使用对应的文字来匹配其类型. 参见#10676获得更加详细的解释.

  • 控制流分析(Control Flow Analysis) - 静态分析中, 使用代码的自然分支(natural branching)和执行路径来更改源码中不同位置的类型
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) - 一种在类型系统中包含变量的方法
function first<T>(array: T[]): T {
  return array[0];
}

这意味着first的返回类型与参数中数组元素的类型相同. (随着时间的流逝, 它们看起来会非常复杂, 但是原理是相同的; 由于单个字母, 它看起来更加复杂.) 泛型函数应在多于一个的位置上使用其类型参数(例如, 在上面, T既用于数组参数的类型, 也用于函数的返回类型). 这是使泛型变得有用的核心 - 它们可以指定两种类型之间的关系(例如, 函数的输出与输入相同, 或者函数的两个输入是相同的类型). 如果一个泛型仅使用其类型参数一次, 那么实际上它根本就不需要泛型, 实际上有些linter会警告说这是一个无用的泛型.

在调用泛型时, 通常可以从函数参数推断出类型参数: first([1, 2, 3]); // 'T' is inferred as 'number'

也可以显式指定, 但最好在可能的情况下使用推断: first<string>(["a", "b", "c"]);

  • 外部类型参数(Outer type parameter) - 在父泛型构造中声明的类型参数:
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) - 将联合类型减少为更少的选项(fewer options).

一个很好的例子是在使用控制流分析时使用--strictNullCheck

// 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) - 与窄转换相反, 获取类型并尝试将其转换为更多的潜在的类型.
const helloWorld = "Hello World"; // Type; "Hello World"

let onboardingMessage = helloWorld; // Type: string

当常量"HelloWorld"在一个可变变量onboardingMessage中被重用时, 得到的类型是"Hello World"的扩展版本: 它从一个字面量类型变成了字符串类型.

Rarely heard

  • 推迟(Deferred) - 类型可能尚无法推断(infer), 因为它正在等待其他类型的推断
  • 同态化(Homomorphic) - 对象之间的映射,这意味着该映射适用于对象的每个属性

JS Internals Specifics

  • 语句(Statement) - "JavaScript应用程序由具有适当语法的语句组成, 单个语句可能会跨越多行. 如果每个语句之间用分号分隔, 则可能会在一行上出现多个语句. 这不是关键字, 而是一组关键字."