如何将赋值 属性 添加到打字稿编译器 api 中的现有对象文字表达式?

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

我想使用打字稿 AST 添加 bar: truex 对象。

此代码生成 bar: true:

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

我知道我需要 return 这个函数才能将它添加到 AST。如果我这样做那么问题是代码将覆盖 foo: true.

有什么想法可以添加 bar: true 而不会丢失 foo: true 吗?

代码:

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

您正在为 ObjectLiteralExpression 节点的访问者返回 PropertyAssignment 节点,该节点替换了完整的对象文字。

相反,您可以更新此节点的属性成员以包含新的 属性:

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

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

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

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

如果您在打字稿中执行此操作,node.properties 是只读的。类型安全的方法是当你想改变当前节点时使用 factory.update*() 方法并且 return 它:

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);
};