As a front-end developer, when developing TS project, the most tedious work should be the data type and mock data of the handwritten interface, because if this part of work is not done, it will be difficult to write the business logic later. Otherwise, it will be all copy and paste similar repetitive work, which is quite time-consuming. Here is a way to automatically generate TS types and mock data to help students get out of their work.

Let’s take a look at the basic process of code generation through an example.

Basic TS code generation process

Let’s take the following ts code as an example to walk through the basic process of generating it.

export interface TestA {
  agenumber;
  name: string; other? :boolean; friends: { sex? :1 | 2;
  },
  catsnumber[];
}
Copy the code

Step 1: Select the data source

Let’s start with a question: What information does it take to generate an interface?

Through analysis, we first need to know how many attributes it has, and then we need to know which attributes are required. In addition, we need to know the type, enumeration and other information of each attribute. There is a data format that can provide us with the data we need perfectly, it is JSON Schema.

JSON Schema is a description of JSON data. For example, we defined the following JSON structure:

{
  "age": 1."name": "Test"."friends": {
  		"sex": 1
  },
  "cats": [
    1.2.3]."other": true
}
Copy the code

Let’s verbally describe this JSON: It has five attributes: age, name, friends, cats and other. the type of age attribute is number, the type of name attribute is string, and the type of cats attribute is arry composed of number. Friends attribute is an object, and it has a sex attribute. The type is numeric, and the type of the other attribute is Boolean.

JSON Schema is described as follows:

{
  "type": "object"."properties": {
    "age": {
      "type": "number"
    },
    "name": {
      "type": "string"
    },
    "cats": {
      "type": "array"."items": {
        "type": "number"}},"friends": {
      "type": "object"."properties": {
        "sex": {
          "type": "number"
        },
        "required": [
          "e"]}},"other": {
	"type": "boolean",},"required": [
      "a"."b"]}}Copy the code

It can be seen that JSON Schema can perfectly implement our oral description programmatically. This example is relatively simple, and the description ability of JSON Schema is far more than this, such as enumeration, the maximum length of array, the maximum and minimum value of number, whether it is necessary and other commonly used attributes can be accurately described. Therefore, it is also commonly used for user input verification scenarios.

Step 2: Select the code generation tool

The TS AST and THE TS Compiler API, which can generate or modify THE TS AST as well as output compiled files. Let’s look at how to use the TS Compiler API to generate the abstract syntax tree and compile it into the code described above.

The corresponding TS Compiler code is as follows:

factory.createInterfaceDeclaration(
    undefined,
    [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
    factory.createIdentifier("TestA"),
    undefined.undefined,
    [
      factory.createPropertySignature(
        undefined,
        factory.createIdentifier("age"),
        undefined,
        factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
      ),
      factory.createPropertySignature(
        undefined,
        factory.createIdentifier("name"),
        undefined,
        factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
      ),
      factory.createPropertySignature(
        undefined,
        factory.createIdentifier("other"),
        factory.createToken(ts.SyntaxKind.QuestionToken),
        factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
      ),
      factory.createPropertySignature(
        undefined,
        factory.createIdentifier("friends"),
        undefined,
        factory.createTypeLiteralNode([factory.createPropertySignature(
          undefined,
          factory.createIdentifier("sex"),
          factory.createToken(ts.SyntaxKind.QuestionToken),
          factory.createUnionTypeNode([
            factory.createLiteralTypeNode(factory.createNumericLiteral("1")),
            factory.createLiteralTypeNode(factory.createNumericLiteral("2"))
          ])
        )])
      ),
      factory.createPropertySignature(
        undefined,
        factory.createIdentifier("cats"),
        undefined,
        factory.createArrayTypeNode(factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword))
      )
    ]
 )
Copy the code

At first glance, generating this simple type of code looks complicated, but on closer inspection the code is much simpler if the methods are wrapped, and there are already some mature third-party libraries, such as TS-MORph.

Ts Compiler Api only has English documents, and it is complicated to use, and it is hard to determine which function needs to be called to generate different types of code, but we can go to Ts AST View to query, and it can generate corresponding abstract syntax tree and Compiler code according to the Ts code you input. The above code is provided by TS AST View.

Factory. CreateInterfaceDeclaration method generates an interface node, after generated, we also need to call a method to print out the generated interface and output file into a string, the reference code is as follows:

// ast goes to code
/ / need to be above the factory. The incoming createInterfaceDeclaration generated nodes
export const genCode = (node: ts.Node, fileName: string) = > {
    const printer = ts.createPrinter();
    const resultFile = ts.createSourceFile(fileName, ' ', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
    const result = printer.printNode(
        ts.EmitHint.Unspecified,
        node,
        resultFile
    );
    return result;
};
Copy the code

Step 3: Beautify the output code

Prettier Prettier already exists in compilers, a necessary tool for every front-end project, which not only formats the file Prettier is writing, but also the string code passed in manually.

import * as prettier from 'prettier';

// The default prettier configuration
const defaultPrettierOptions = {
    singleQuote: true.trailingComma: 'all'.printWidth: 120.tabWidth: 2.proseWrap: 'always'.endOfLine: 'lf'.bracketSpacing: false.arrowFunctionParentheses: 'avoid'.overrides: [{files: '.prettierrc'.options: {
                parser: 'json',}}, {files: 'document.ejs'.options: {
                parser: 'html',},},],};// Format and beautify the file
type prettierFileType = (content:string) = > [string.boolean];
export const prettierFile: prettierFileType = (content:string) = > {
    let result = content;
    let hasError = false;
    try {
        result = prettier.format(content, {
            parser: 'typescript'. defaultPrettierOptions }); }catch (error) {
        hasError = true;
    }
    return [result, hasError];
};
Copy the code

Step 4: Write the generated code to our file

This step is relatively simple, directly have node to provide the FS Api generated file, the code is as follows:

// Create directory
export const mkdir = (dir:string) = > {
    if (!fs.existsSync(dir)) {
        mkdir(path.dirname(dir));
        fs.mkdirSync(dir);
    }
};
/ / write file
export const writeFile = (folderPath:string, fileName:string, content:string) = > {
    const filePath = path.join(folderPath, fileName);
    mkdir(path.dirname(filePath));
    const [prettierContent, hasError] = prettierFile(content);
    fs.writeFileSync(filePath, prettierContent, {
        encoding: 'utf8'});return hasError;
};
Copy the code

Front and rear end coordination

An important step is missing from the above process: Who provides the data source JSON Schema?

This requires the cooperation of the front and back end. At present, there are mature tools for generating JSON Schema on the back end, such as Swagger, YAPI, etc.

Swagger. Json file can be provided to the front-end with Swagger. The content of the file includes detailed data of all interfaces, including JSON Schema data.

Different from Swagger, YAPI is the centralized management platform of API. We can obtain the detailed data of all API managed on it through the interface provided by it, which is almost the same as the content provided by Swagger. Moreover, YAPI platform supports importing or generating Swagger.

If there is an interface management platform and relevant specifications are formulated, the collaboration efficiency of the front and back ends will be greatly improved and communication costs will be reduced. Moreover, the front end can also do some work related to engineering efficiency based on the management platform.

To overcome difficulties

The above steps are only a brief introduction to the generation of ts type code, which has some difficulties to solve, such as:

  • The TS Compiler API does not generate comments: this problem can be solved by inserting comments manually after string generation.
  • The type of actual business can be very complex and deeply nested: this problem can be solved by recursive functions.
  • What should I do if the generated type code is changed in the API, or if the new API needs to be placed in the same file as the original generated code? TS ComPiler API can read source files, even existing files can be read. We can read source files and then modify its abstract syntax tree using ComPiler API to realize the function of modifying or adding types.
  • The coordination problem of the front and back ends: This needs to be solved by the leader.

conclusion

Through the four steps mentioned above, we have learned the basic process of generating code, and the implementation of each step is not fixed, you can choose your own:

  • In terms of data source selection, in addition to JSON Schema, we can also choose the original JSON data as the data source, but the generated type is not so precise. Here we recommend a very useful website: JSON2TS.
  • Code generation tools We can also use some common template engines to generate, such as Nunjucks, EJS, etc., they can generate not only HTML, but also any file format, and can return the generated string.
  • Prettier is still recommended for code beautification.
  • For the front-end, Node is currently the best way to export files.

This article only provides an idea of engineering to generate TS type, Mock data and other simple replicable code, which can reduce some of the labor intensive work and let us focus on business logic development.

reference

  • TS Compiler API
  • Prettier