运行Typescript编译器插件/转换器后进行类型检查

时间:2018-11-23 14:39:49

标签: typescript plugins transformer

我正在关注一个博客(https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943),该博客有关如何编写Typescript编译器插件/变压器。

在应用了第一个简单的转换之后,该转换会引入类型错误(在没有该属性的对象上访问的某些属性),我注意到没有显示类型错误。实际上,编译器可以正常进行。

services:
    audit.persister.base:
        class: MyBundle\Security\Audit\Persister\ChainedEntityTrailPersister
        calls:
            - ['addPersister', ['@audit.persister_elasticsearch']]

应用于import * as ts from "typescript"; export const transformerFactory = ( program: ts.Program ): ts.TransformerFactory<ts.SourceFile> => { return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => { const visitor: ts.Visitor = (node: ts.Node): ts.VisitResult<ts.Node> => { if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) { if (node.expression.escapedText === "someCall") { return ts.createCall( ts.createPropertyAccess(node.expression, "nonExisting"), node.typeArguments, node.arguments ); } } return ts.visitEachChild(node, visitor, context); }; return (sf: ts.SourceFile) => ts.visitNode(sf, visitor); }; };

index.ts

收益declare function someCall(...args: any[]): string; console.log(someCall(1, 2, true));

index.js

(即使使用console.log(someCall.nonExisting(1, 2, true));

这是预期的行为吗?我可以在某处启用此功能吗?

2 个答案:

答案 0 :(得分:2)

  

这是预期的行为吗?

是的

  

这可以在某处启用吗?

不,变压器的用途有限。不支持编译器的通用通用“插件”。

变压器作为“发射”阶段的一部分运行,该阶段从经过类型检查的AST生成JavaScript代码。

This comment在变压器PR中说

  

所有这些转换都是在检查阶段发生之后发生的

更新

  

是否有某种方法可以编译两次:一次转换文件,然后一次对整个项目进行类型检查?我不在乎是否需要对转换后的文件进行单独检查。

我不知道。尝试做的第一件事是让您的转换器像以前一样修改AST,然后通过调用

来手动检查修改过的文件
program.getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile)

({getDiagnostics具有第二个参数-cancellationToken-但似乎可以忽略它,因为在类型检查器代码中始终会对其进行检查以防undefined。通常,您可以看看如何在自己的源代码中使用各种编译器API,例如emit首先通过调用各种program.getNNNDiagnostics进行类型检查,然后运行带有转换的发射器。)

这可能行不通,因为类型检查器会修改AST,这取决于AST处于正确的状态。

然后,您可能需要查看builder API-目的是观察源文件的修改并重新编译更改的文件(source code link)。我不知道要在AST修改上对其进行重新编译将有多么困难,而且看来您将无法使用变形金刚中的可用访问者;您将不得不手动遍历AST。

还有ts-simple-ast库,其声明的目的是“提供一种导航和操作TypeScript和JavaScript代码的简单方法”。我自己没有使用过它,也不知道它对您的目标有多有用。

答案 1 :(得分:0)

您只需要创建一个CompilerHost并将其getSourceFile方法设置为指向转换后的源文件即可。一种方法是通过从文件名到转换后的源文件的映射。之后,CompilerHost的创建将类似于:

const compilerHost = ts.createCompilerHost(compilerOptions);
    const defaultLibFileName = ts.getDefaultLibFileName(compilerOptions);
    compilerHost.getSourceFile = (sourceName) => {
      let sourcePath = sourceName;
      if (sourceName === defaultLibFileName) {
        sourcePath = ts.getDefaultLibFilePath(compilerOptions);
      } else if (this.sourceFileMap.has(sourceName)) {
        return this.sourceFileMap.get(sourceName);
      }
      if (!fs.existsSync(sourcePath)) {
        return undefined;
      }
      const contents = fs.readFileSync(sourcePath, 'utf-8');
      return ts.createSourceFile(sourceName, contents, COMPILER_OPTIONS.target);
    };

然后,您只需要将此CompilerHost作为第三个参数传递给ts.createProgram()