I created a typescript transformer that traverses the AST and changes array calls from
myArr.push(el)
to Array.prototype.push.call(myArr, args)
This works well for all cases apart the case where the array is using the this
key word inside a class:
this.myArr.push(el)
transforms to Array.prototype.push.call(this.myArr, args)
while the correct transformation should be:
this.myArr.push(el)
to Array.prototype.push.call(_this.myArr, args)
(the _this
having been already created by the ts-loader plugin)
How can I achieve this?
This is the code of my transformer:
function getType(type) {
if (type && type.symbol && type.symbol.name) {
return type.symbol.name;
} else if (
type &&
type.literalType &&
type.literalType.symbol &&
type.literalType.symbol.name
) {
return type.literalType.symbol.name;
}
return null;
}
exports.__esModule = true;
const { ClassificationTypeNames } = require("typescript");
var ts = require("typescript");
var transformer = function (typechecker) {
return function (context) {
var visitor = function (node) {
// 1. First check: chained expression
// if it's array.filter().join().map() then I want to change only the first part
// meaning
// if property access is a call expression - ignore and dont change
if (
ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression) &&
ts.isCallExpression(node.expression.expression)
) {
return ts.visitEachChild(node, visitor, context);
}
if (
ts.isCallExpression(node) &&
ts.isPropertyAccessExpression(node.expression)
) {
const type = typechecker.getTypeAtLocation(node.expression.expression);
const typeNameS = getType(type);
if (typeNameS === "Array") {
const methodName = node.expression.name.getText();
const callArgs = node.arguments;
const identifier = node.expression.expression.getText();
return ts.createCall(
ts.createPropertyAccess(
ts.createPropertyAccess(
ts.createPropertyAccess(
ts.createIdentifier("Array"),
ts.createIdentifier("prototype")
),
ts.createIdentifier(methodName)
),
ts.createIdentifier("call")
),
undefined,
[ts.createIdentifier(identifier), ...callArgs]
);
}
}
return ts.visitEachChild(node, visitor, context);
};
return function (node) {
return ts.visitNode(node, visitor);
};
};
};
Found the error.
I was getting the identifier and creating it manually.
and then
The correct way to construct the new node is to pass
node.expression.expression
directly to the construction call.and that way the typescript engine knows how to build the
_this
andthis
references correctly.