Consider the following TypeScript file:
class A {
private x? = 0;
private y? = 0;
f() {
console.log(this.x, this.y);
delete this.x;
}
}
const a = new A();
a.f();
When building it in webpack using awesome-typescript-loader:
{
test: /\.tsx?$/,
include: path.resolve("./src"),
exclude: path.resolve("./node_modules/"),
use: {
loader: 'awesome-typescript-loader',
options: {
getCustomTransformers: program => ({
before: [deleteTransformer(program)]
})
}
}
},
In this configuration, the custom transformer deleteTransformer
is invoked to replace any delete
expression with delete this.y
:
import * as ts from "typescript";
export default function getCustomTransformers(program: ts.Program): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => (file: ts.SourceFile) => visitNodeAndChildren(file, program, context);
}
function visitNodeAndChildren<N extends ts.Node>(node: N, program: ts.Program, context: ts.TransformationContext): N {
return ts.visitEachChild(visitNode(node, program), childNode => visitNodeAndChildren(childNode, program, context), context);
}
function visitNode<N extends ts.Node>(node: N, program: ts.Program): N {
if (ts.isDeleteExpression(node)) {
return ts.factory.createDeleteExpression(ts.factory.createPropertyAccessExpression(
ts.factory.createThis(),
"y",
)) as ts.Node as N;
}
return node;
}
After compiling, the expected code is generated where y
is deleted instead of x
:
/***/ "/7QA":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var A = /** @class */ (function () {
function A() {
this.x = 0;
this.y = 0;
}
A.prototype.f = function () {
console.log(this.x, this.y);
delete this.y;
};
return A;
}());
var a = new A();
a.f();
/***/ }),
However, changing the property name from y
to
z</code, which does not exist on class <code>A
, does not produce an error message.
If you modify class A
to have a non-optional x
property and keep y
in the transformer, an error occurs during compilation:
× 「atl」: Checking finished with 1 errors
ERROR in [at-loader] ./src/index.ts:7:16
TS2790: The operand of a 'delete' operator must be optional.
These observations lead to the understanding that the transformer is applied after the code is checked. Despite being included in the before
section, TypeScript validates the original code rather than the transformed code.
If you're wondering about the differences between before
and after
transformers in the getCustomTransformers
object, testing both showed no noticeable distinction. To apply transformations before code checking occurs, further investigation into TypeScript's transformation process may be necessary.