Как добавить новое свойство в класс TypeScript с помощью API компилятора TypeScript?

Я пытаюсь добавить новое свойство в свой 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 {
    // ...
  }
}

Я хочу добавить новое свойство 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 {
    // ...
  }
}

В моей функции правил 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;
    }
}

А вот функция updateModelFields():

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
  // ??
}

Я пробовал несколько способов добавить объявление нового свойства, но всегда терпел неудачу.

Когда я попытался добавить с помощью функции splice(), он сказал:

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

Любая идея или лучшая практика?


person netdjw    schedule 29.09.2020    source источник


Ответы (1)


Обычно при использовании API преобразования это будет использоваться с функцией ts.transform, а затем использовать factory.updateClassDeclaration для добавления свойства в класс. Когда у вас есть окончательный преобразованный AST, вы можете распечатать его в строке с помощью принтера (ts.createPrinter).

Тем не менее, API преобразования не был разработан для этой цели - он предназначен для преобразования кода TS в JS - и поэтому он не очень хорош для изменения существующих файлов TypeScript. Например, если вы преобразовываете AST, а затем распечатываете его, вы потеряете информацию о форматировании, и это может нарушить существующие комментарии.

По этой причине я бы предложил вместо этого использовать API изменения текста (см. _4 _) - это то, что используется для быстрых исправлений, - или, проще говоря, просто вставьте текст свойства непосредственно в нужную позицию в строке. Вы можете определить, куда вставить, основываясь на положениях окружающих узлов. Например:

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

В качестве альтернативы вы можете просто использовать ts-morph чтобы справиться с этим за вас, поскольку довольно просто вставить свойства в класс используй это.

person David Sherret    schedule 29.09.2020