How to add new property to a TypeScript class with TypeScript compiler API?

馋奶兔 提交于 2021-02-10 14:14:54

问题


I try to add new propery to my awesome.model.ts file.

The original content is like this:

import { TagInterface, TagUIFieldsInterface } from './tag.interface';

export class Tag implements TagInterface {
  readonly api_endpoint = '/tag';
  id: ID;
  name: string;

  fields: FieldContainerInterface<TagUIFieldsInterface> = {
    // ...
  };

  init(data?: any): TagInterface {
    // ...
  }
}

I want to add a new propery color_code: string; after the name property's line. To look like this:

import { TagInterface, TagUIFieldsInterface } from './tag.interface';

export class Tag implements TagInterface {
  readonly api_endpoint = '/tag';
  id: ID;
  name: string;
  color_code: string;

  fields: FieldContainerInterface<TagUIFieldsInterface> = {
    // ...
  };

  init(data?: any): TagInterface {
    // ...
  }
}

In my Schematics rule function I tried this, but I'm stucked:

export function model(_options: Schema, _fields?: Field[]): Rule {
  return (tree: Tree, _context: SchematicContext) => {
    // ...

    if (tree.exists(file)) {
      // read the file content and convert it to ts.Node[]
      const text = tree.read(file) ?? '';
      let sourceText = text.toString('utf-8');
      const sourceFile = ts.createSourceFile(file, sourceText, ts.ScriptTarget.Latest, true);
      const nodes = getSourceNodes(sourceFile);

      updateModelFields(file, nodes, _options);

      return;
    }
}

And here is the updateModelFields() function:

export function updateModelFields(file: string, nodes: ts.Node[], options: Schema) {
  // find field definitions
  let propertyNodes: ts.Node[] = nodes.filter(n => n.kind === ts.SyntaxKind.PropertyDeclaration) || [];

  // create new property declaration
  const propertyDeclaration = ts.factory.createPropertyDeclaration(
    undefined,
    undefined,
    'color_code',
    undefined,
    ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
    undefined
  );

  // add propertyDeclaration to nodes
  // ??
}

I tried several ways to add the new property declaration, but always get faild.

When I tried to add with splice() function it said:

Error: Debug Failure. False expression: Node must have a real position for this operation

Any idea or best practice?


回答1:


Generally if using the transformation API, this would be used with the ts.transform function then use factory.updateClassDeclaration to add the property to the class. Once you have the final transformed AST, you can print it out to a string by using the printer (ts.createPrinter).

That said, the transformation API wasn't designed for this purpose—it's meant to transform TS code to JS—and so it's not great at modifying existing TypeScript files. For example, if you transform an AST then print it out, you will lose your formatting information and it might trample over existing comments.

For that reason, I would suggest instead to use the text change API (see SourceFile#update)—this is what is used for quick fixes—or more simply just insert the property text into the right position in the string directly. You can figure out where to insert based on the surrounding node positions. For example:

const classDec = sourceFile.statements.find(ts.isClassDeclaration)!;
const nameProp = classDec.members
    .find(c => ts.isPropertyDeclaration(c) && c.name.getText(sourceFile) === "name")!;

// Assumes that the name property node does not have any trailing comments...
// You can use ts.getTrailingCommentRanges to figure that out and use
// the last comment end position instead of `nameProp.end`
sourceText = sourceText.substring(0, nameProp.end)
    + "\n  color_code: string;"
    + sourceText.substring(nameProp.end);

Alternatively, you may just want to use ts-morph to handle this for you as it's quite easy to insert properties into a class using it.



来源:https://stackoverflow.com/questions/64125608/how-to-add-new-property-to-a-typescript-class-with-typescript-compiler-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!