检查器

如前所述,检查器使得 TypeScript 更独特,比其它 JavaScript 转译器更强大。检查器位于 checker.ts 中,当前有 23k 行以上的代码(编译器中最大的部分)

程序对检查器的使用

检查器是由程序初始化,下面是调用栈示意(绑定器一节也展示过):

  1. program.getTypeChecker ->
  2. ts.createTypeChecker(检查器中)->
  3. initializeTypeChecker(检查器中) ->
  4. for each SourceFile `ts.bindSourceFile`(绑定器中)
  5. // 接着
  6. for each SourceFile `ts.mergeSymbolTable`(检查器中)

与发射器的联系

真正的类型检查会在调用 getDiagnostics 时才发生。该函数被调用时(比如由 Program.emit 请求),检查器返回一个 EmitResolver(由程序调用检查器的 getEmitResolver 函数得到),EmitResolvercreateTypeChecker 的一个本地函数的集合。介绍发射器时还会再次提到。

下面是该过程直到 checkSourceFile 的调用栈(checkSourceFilecreateTypeChecker 的一个本地函数):

  1. program.emit ->
  2. emitWorker (program local) ->
  3. createTypeChecker.getEmitResolver ->
  4. // 第一次调用下面的几个 createTypeChecker 的本地函数
  5. call getDiagnostics ->
  6. getDiagnosticsWorker ->
  7. checkSourceFile
  8. // 接着
  9. return resolver
  10. // 通过对本地函数 createResolver() 的调用,resolver 已在 createTypeChecker 中初始化。

全局命名空间合并

initializeTypeChecker 中存在以下代码:

  1. // 初始化全局符号表(SymbolTable)。
  2. forEach(host.getSourceFiles(), file => {
  3. if (!isExternalModule(file)) {
  4. mergeSymbolTable(globals, file.locals);
  5. }
  6. });

基本上是将所有的 global 符号合并到 let globals: SymbolTable = {} 符号表中(位于 createTypeChecker 中)。mergeSymbolTable 主要调用 mergeSymbol 函数。

检查器错误报告

检查器使用本地的 error 函数报告错误,如下所示:

  1. function error(location: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
  2. let diagnostic = location
  3. ? createDiagnosticForNode(location, message, arg0, arg1, arg2)
  4. : createCompilerDiagnostic(message, arg0, arg1, arg2);
  5. diagnostics.add(diagnostic);
  6. }

原文: https://jkchao.github.io/typescript-book-chinese/compiler/checker.html