跳到主要内容

检查器

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

程序对检查器的使用

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

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

与发射器的联系

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

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

program.emit ->
emitWorker (program local) ->
createTypeChecker.getEmitResolver ->
// 第一次调用下面的几个 createTypeChecker 的本地函数
call getDiagnostics ->
getDiagnosticsWorker ->
checkSourceFile

// 接着
return resolver
// 通过对本地函数 createResolver() 的调用,resolver 已在 createTypeChecker 中初始化。

全局命名空间合并

initializeTypeChecker 中存在以下代码:

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

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

检查器错误报告

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

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