# Writing a TypeScript Code Generator: Templates vs AST

Code generators replace mindless coding with productivity; a pure function that produces predictable code, given a (set of) input(s). Generating boilerplate classes or templates — a common use case for Codegen.

# **Use-case: Generating Classes**

We will be generating components (Class) that follow a repetitive yet predictable pattern.

```typescript
export class HttpBinApi implements ICredentialType {
 name = 'httpbinApi';
 displayName = 'HttpBin API';
}
```

Notice how many times the developer has to rewrite “HTTP Bin API” in different cases and styles? Moreover, using the wrong case would cause linter issues and never-ending headaches. Could this be automated?

# **Approach: 🧵 String Manipulation**

Simply using JavaScript String functions can take us a long way. Template Strings, joins and possibly a little bit of `lodash` .

```typescript
function generateClass(name: string) {
    return [
        `export class ${pascalCase(name)}}Api implements ICredentialType {`,
        `  name = '${camelCase(name)}Api';`,
        `  displayName = '${titleCase(name)} API';`,
        `}`,
    ].join('\n');
}
```

While this works, it makes me **uncomfortable**; my mind implicitly sees fragility in this approach. What if the code generator is **more complex**? What if we need to iterate through **arrays or objects**? What if the signatures **change**? It seems **unmaintainable**.

## **Hygen**

A more practical approach to string manipulation and using templates to generate code — could be [Hygen](https://www.hygen.io/). It handles file creation, manipulation of existing files gracefully.

The devs call it the *“The scalable code generator that saves you time.”* It is a code generation utility that integrates seamlessly into your development flow. It uses EJS templates to define your file structures.

```typescript
---
to: src/credentials/<%= h.changeCase.pascal(name) %>.ts
---

export class <%= h.changeCase.pascal(name) %> implements ICredentialType {
    name = "<%= h.changeCase.camel(name) %>";
    displayName = "<%= name %>";
}
```

Check it out here: [Hygen](https://www.hygen.io/)

> *Manipulating whitespace dependent languages like YAML or Python could be challenging with Hygen. — A dev who tried.*

Let’s admit it, in our mind, *“code generation is supposed to be a bit more complicated”*. The simplicity here may be causing the nerves!

# **Approach: 🌲 Abstract Syntax Trees**

## **Introduction**

What if we could have a more **programmatic-way** of generating code? Instead of having *apparently meaningless* string concatenations, we had **semantics and meanings**?

Introducing ASTs. Code compilers usually do not compile strings of code directly. Instead, it can build up a tree-like structure of branches and objects and conditions.

![](https://miro.medium.com/v2/resize:fit:1400/1*N9Y26_mQ-ZQ5jXMSMDyEKg.png align="left")

A tree representation of a simple TypeScript class

## **Example of AST**

For the HttpBinApi class mentioned at the top, the AST representation may look something like this:

```bash

  factory.createClassDeclaration(
    [factory.createToken(ts.SyntaxKind.ExportKeyword)],
    factory.createIdentifier("HttpBinApi"),
    //...
    [
      factory.createPropertyDeclaration(
        factory.createIdentifier("name"),
        //...
        factory.createStringLiteral("httpbinApi")
      ),
      factory.createPropertyDeclaration(
        factory.createIdentifier("displayName"),
         //...
        factory.createStringLiteral("HttpBin API")
      )
    ]
  );
```

> *Do check out the* [***TypeScript AST Viewer***](https://ts-ast-viewer.com/#code/KYDwDg9gTgLgBAYwDYEMDOa4AkYzAIQEsA7AQTELkIFswlhrhiZMBJAYSmABMmZCUSACoBPMMDgBvAFBxiKRnAC8cAOQALXGABGJcoVUBuWd0Jo6KEQDkFElapx4ixOKQAKrI9IC+QA) *to play around with this Syntax Tree.*

## **Exploring ASTs on our own 🕵🏽**

![](https://miro.medium.com/v2/resize:fit:1400/1*Djs2NKXLO0Mrm00qiNW-GA.png align="left")

TypeScript AST Viewer in Action

Okay we went from string manipulation to extra-terrestrial programming in a paragraph. I encourage you to use the AST Viewer and click around for now. A few questions to get you started:

1. What does a `const` look like?
    
2. How about a `let` ?
    
3. A simple `console.log` ?
    
4. How about adding two numbers?
    
5. Okay, looping through an array of numbers?
    

## **AST to Code? 👩🏻‍💻**

All along, we’ve been converting TypeScript code to AST. But how do we do the opposite? Reconstructing TypeScript code is easy with the help of the built-in `ts.Printer` .

Step 1: Create a Printer.

```typescript
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
```

Step 2: Create an Instance of a `SourceFile` .

```typescript
const sourceFile = ts.createSourceFile(
    'HttpBinApi.ts',
    '',
    ts.ScriptTarget.Latest,
    false, // setParentNodes
    ts.ScriptKind.TS,
  );
```

Step 3: Render as String.

```typescript
printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
```

![](https://miro.medium.com/v2/resize:fit:1222/1*nCWD4ax4QmvdymOhz2aXVg.png align="left")

Check out a fully working, [**in-the-browser example** right here: StackBlitz #1](https://stackblitz.com/edit/stackblitz-starters-7ncslx). Do feel free to play around with it!

## **Where is the Code-Generator? ⚙️**

Ah yes yes, now we can assemble everything together into a semi-working code generator.

Step 1: Parametrised the AST representation.

```typescript
const className = `${_.upperFirst(_.camelCase(name))}Api`;
const classIdentifier = factory.createIdentifier(className);
```

Step 2: With my trusty Co-Pilot, a large language model — I have refactored the code to make more legible.

Check out the [refactored version: **StackBlitz #2**](https://stackblitz.com/edit/stackblitz-starters-bsn3ks?file=index.ts).

```typescript
console.log(generateClass('Cloud File Transfer'));
```

```typescript
❯ ts-node index.ts
export class CloudFileTransferApi implements ICredentialType {
    name = "cloudFileTransferApi";
    displayName = "Cloud File Transfer";
}
```

There are no clear-cut winners here. Based on your requirements, you do need to make a call on **simplicity vs robustness**. Hope you had fun! 🎉

# **References and Links to Check-out**

1. [Hygen | Hygen](https://www.hygen.io/) — Code Generation Framework based on EJS Templates
    
2. [Using the Compiler API · microsoft/TypeScript Wiki (github.com)](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API)
    
3. [Typescript Code Generation — DEV Community](https://dev.to/rametta/typescript-code-generation-epn)
    
4. [TypeScript AST Viewer (ts-ast-viewer.com)](https://ts-ast-viewer.com/#)
    
5. [AST explorer](https://astexplorer.net/)
    
6. [TypeScript AST Exploration — StackBlitz](https://stackblitz.com/edit/stackblitz-starters-7ncslx?file=index.ts)
    
7. [TypeScript AST Exploration: Parametrizing — StackBlitz](https://stackblitz.com/edit/stackblitz-starters-bsn3ks?file=index.ts)
