问题
I'm following a blog (https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943) on how to write a Typescript compiler plugin/transformer.
After applying a first simple transformation which should introduce a type-error (some property accessed on an object that doesn't have that property) I noticed that the no type-error is shown. In fact, the compiler proceeds as normal.
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);
};
};
Applied to index.ts
:
declare function someCall(...args: any[]): string;
console.log(someCall(1, 2, true));
Yields index.js
:
console.log(someCall.nonExisting(1, 2, true));
(even with noEmitOnError: true
)
Is this intended behavior? Is this something I can enable somewhere?
回答1:
Is this intended behavior?
Yes.
Is this something I can enable somewhere?
No, transformers have limited purpose. General all-purpose "plugins" for the compiler are not supported.
Transformers are run as part of the "emit" phase which generates javascript code from type-checked AST.
This comment in the transformers PR says
Transforms, all of them, happen after the checking phase has happened
UPDATE
is there some way to compile twice: once to transform the file and once to type-check the whole thing? I don't mind if I have to run a separate check for the transformed files.
I don't know. The first thing to try is to have your transformers modify AST as before, then type-check modified files manually by calling
program.getDiagnosticsProducingTypeChecker().getDiagnostics(sourceFile)
(getDiagnostics
has second parameter - cancellationToken
- but it seems that it's safe to omit it because it's always checked against being undefined
in the type checker code. In general, you can look how various compiler APIs are used in its own source code, for example emit does type-checking first by calling various program.getNNNDiagnostics
, then runs emitter with transforms.)
This might or might not work, because type checker modifies AST, and it depends on AST being in the correct state.
Then, you might want to look at the builder API - it's purpose is to watch for source file modifications and recompile changed files (source code link). I don't know how hard it would be to make it recompile on AST modifications, also it looks like you would not be able to use visitors available in tranformers; you'll have to traverse AST manually.
Also, there's ts-simple-ast library, whose stated purpose is to "Provide a simple way to navigate and manipulate TypeScript and JavaScript code". I haven't used it myself and have no idea how useful it is for your goal.
回答2:
You just need to create a CompilerHost
and set its getSourceFile
method to point to your post-transform source files. One way to do this is by having a Map from file names to transformed source files. Afterwards the CompilerHost
creation will look something like:
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);
};
Then you just need to pass this CompilerHost
as the third argument to ts.createProgram()
来源:https://stackoverflow.com/questions/53448691/typechecking-after-running-typescript-compiler-plugin-transformer