Search code examples
typescriptabstract-syntax-tree

How to add assignment property to existing object literal expression in typescript compiler api?


I want to add bar: true to x object using typescript AST.

This code cerates the bar: true:

 factory.createPropertyAssignment(
   factory.createIdentifier("bar"),
   factory.createTrue()
 );

I know I need to return this function in order to add it to AST. if I do so then the problem is the code will override the foo: true.

Any ideas how to add bar: true without lose the foo: true?

The code:

import * as ts from "typescript";

const code = `
const x = {
  foo: true
})
`;

const node = ts.createSourceFile("x.ts", code, ts.ScriptTarget.Latest);

const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });

export const add = (context) => (rootNode) => {
  function visit(node) {
    const { factory } = context;

    // if (node.kind === ts.SyntaxKind.ObjectLiteralExpression) {
    //   return factory.createPropertyAssignment(
    //     factory.createIdentifier("bar"),
    //     factory.createTrue()
    //   );
    // }

    return ts.visitEachChild(node, visit, context);
  }

  return ts.visitNode(rootNode, visit);
};

const result = ts.transform(node, [add]);

// console.log({ result });
const transformedSourceFile = result.transformed[0];

const out = printer.printFile(transformedSourceFile);

console.log({ out });

codesandbox.io


Solution

  • If you're doing this in typescript, node.properties is readonly. The type-safe way is to use the factory.update*() methods when you want to mutate the current node and return it:

    export const add: ts.TransformerFactory<ts.SourceFile> = (context) => (rootNode) => {
        function visit(node: ts.Node): ts.VisitResult<ts.Node> {
            const { factory } = context;
    
            // using the provided typeguard to narrow the node kind
            if (ts.isObjectLiteralExpression(node)) {
                return factory.updateObjectLiteralExpression(node, [
                        // include the existing properties
                        ...node.properties,
                        // add your generated property
                        factory.createPropertyAssignment(
                            factory.createIdentifier("bar"),
                            factory.createTrue()
                        )
                    ]
                );
            }
            return ts.visitEachChild(node, visit, context);
        }
    
        return ts.visitNode(rootNode, visit);
    };