如何使用 TypeScript 编译器 API 向 TypeScript 类添加新属性?

net*_*djw 2 typescript typescript-compiler-api

我尝试向我的文件添加新属性awesome.model.ts

原来的内容是这样的:

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 {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

我想在属性行color_code: string;之后添加一个新属性。name看起来像这样:

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 {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

在我的 Schematics 规则函数中,我尝试了这个,但我被困住了:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是updateModelFields()函数:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试了多种方法来添加新的属性声明,但总是失败。

当我尝试添加splice()功能时,它说:

Error: Debug Failure. False expression: Node must have a real position for this operation
Run Code Online (Sandbox Code Playgroud)

有什么想法或最佳实践吗?

Dav*_*ret 5

一般来说,如果使用转换 API,这将与ts.transform函数一起使用,然后用于factory.updateClassDeclaration将属性添加到类中。获得最终转换后的 AST 后,您可以使用打印机 ( ts.createPrinter) 将其打印为字符串。

\n

也就是说,转换 API 并不是为此目的而设计的\xe2\x80\x94,它的目的是将 TS 代码转换为 JS\xe2\x80\x94,因此它不适合修改现有的 TypeScript 文件。例如,如果您转换 AST 然后将其打印出来,您将丢失格式信息,并且可能会破坏现有注释。

\n

因此,我建议改用文本更改 API(请参阅SourceFile#update)\xe2\x80\x94,这是用于快速修复的\xe2\x80\x94,或者更简单地将属性文本插入到字符串中的正确位置直接地。您可以根据周围的节点位置来确定插入位置。例如:

\n
const classDec = sourceFile.statements.find(ts.isClassDeclaration)!;\nconst nameProp = classDec.members\n\xc2\xa0 \xc2\xa0 .find(c => ts.isPropertyDeclaration(c) && c.name.getText(sourceFile) === "name")!;\n\n// Assumes that the name property node does not have any trailing comments...\n// You can use ts.getTrailingCommentRanges to figure that out and use\n// the last comment end position instead of `nameProp.end`\nsourceText = sourceText.substring(0, nameProp.end)\n\xc2\xa0 \xc2\xa0 + "\\n \xc2\xa0color_code: string;"\n\xc2\xa0 \xc2\xa0 + sourceText.substring(nameProp.end);\n
Run Code Online (Sandbox Code Playgroud)\n

或者,您可能只想使用ts-morph来为您处理这个问题,因为使用它可以很容易地将属性插入到类中。

\n