TypeScript code generation using it’s compiler API

Max Koverdyaev
3 min readMar 12, 2021

--

There are many cases when we use code generation. TypeScript-based projects are not exceptions. TypeScript itself has a rich compiler API for these kind of things. The compiler API is not well documented but it’s quite concise and intuitive.

In this article I want to focus on generating type declarations as I find this case very useful when we work with external systems which provide schemas about their types.

To start using the TypeScript compiler API we need a Node.js-based with installed the typescript package. In examples I use TypeScript as the primary language to have a complete IDE experience. I also use the ts-node package to run TypeScript code without additional building setup.

A typical boilerplate to start code generation looks like this:

We import typescript package and create a source file and printer. Indeed there are many option how you can configure them but seem they are more related to internal typescript facilities and not very useful for a more generic tasks.

Typescript compiler API has the factory that allows you to create any syntax entity of the language. It’s accessible via ts.factory property. TypeScript syntax abilities are very fancy. You can create statements, expressions, declarations and so on. As there’s no much examples for every ts.factory method I’d recommend to search for syntax entity names on the Internet. I often look for the official TypeScript handbook to understand the language syntax terms when needed.

Type alias generation

In TypeScript a type alias is nothing but another name for an existing type. A type alias can be used in a way of documentation. A good example is the Uuid type which can be provided by an external API, but there’s no simple approach to represent it the TypeScript type system. For simplicity we can describe such alias:

It won’t add any type checks but can help a developer to understand what kind of string they have to provide to an external system.

Let’s generate this simple type alias:

We need to create a type reference for the primitive string type and the ts.factory.createTypeReferenceNode function will help with it. The next step is to make a type alias itself with the ts.factory.createTypeAliasDeclaration function. As you see no much explanation required. The output will show the desired result.

Union type generation

Union types in TypeScript represent an algebraic sum type when a value can be one of the given types. A simple example is a type when it’s value can be a string or a number :

In order to generate this kind of a type we need to extended the previous code snippet:

We add a type reference for the number type and then call the ts.factory.createUnionTypeNode function to create a union from the string and number types.

Generic type generation

This example will demonstrate how to generate a generic type definition. Also let’s look at the ability to generate an index access on a type. The best candidate for this purpose is a basic dictionary type.

Here’s how we can describe a generic dictionary type in TypeScript:

And the implementation of the dictionary type generation:

Step-by-step we create the dictionary generic type parameter identifier, parameter declaration, index signature ([key: string]: T) and dictionary interface declaration.

POCO-like type generation

Let’s try to generate a more complex type declaration. In the next we will create a typical POCO-like type declaration and put in a namespace.

Here’s a reference we want to produce:

And the generation code facility for this reference:

Firstly we produce the Product type properties via the ts.factory.createPropertySignature function. You can see how to create property modifiers in this part of the code, also the generation of array types and optional property tokens. After the properties are generated the type interface is produced using the ts.factory.createInterfaceDeclaration function like in the previous example. And finally we build the namespace using the ts.factory.createModuleDeclaration function. After all the expected result will be printed to the console.

So that’s a basic coverage of how we can use the Typescript compiler API to generate type declarations. If you have heterogeneous info systems and need to write integration boilerplate between frontends and backends you may find a way to automate it using code generation and Typescript.

P.S.: You can find all examples here.

--

--