Now many JS libraries are written in typescript, and you almost always have to ask typescript in an interview. You may be familiar with the syntax and built-in advanced types of TS, and you have no problem with ts configuration and command-line usage, but you feel that you don’t understand TS very well. Suffering from no good way to continue to improve. I recommend researching the typescript Compiler API.

Typescript parses ts source code into an AST, performs various conversions on the AST, and generates JS code, checking the AST for type. Typescript encapsulates this whole process in TSC’s command-line tool, which is where we compile TS code and type check.

But in addition to providing TSC’s command-line tools, TS also exposes a lot of APIS and allows you to customize Transformer. Just as Babel can compile ESNext, TS syntax to JS, write Babel plug-ins to convert code, and expose various apis. It’s just that typescript Transformer’s ecosystem isn’t nearly as good as Babel’s, and fewer people know about it.

Typescript Transformer does some things that Babel doesn’t:

  • Babel is converted from TS, exnext, etc to JS. The generated JS code will lose the type information and cannot generate TS code.

  • Babel just converts TS code and does no type checking.

Both of these Babel plug-ins can be done with typescript Transformer.

In addition, learning the API of typescript Compiler can help you drill into the typescript compilation process and get a better grasp of typescript.

With that said, let’s get started with typescript Transformer with an example.

Case description

Ts code like this:

type IsString<T> = T extends string ? 'Yes' : 'No';

type res = IsString<true>;
type res2 = IsString<'aaa'>;
Copy the code

We would like to be able to calculate the values of the res and RES2 types and add comments at the end.

Like this:

type IsString<T> = T extends string ? 'Yes' : 'No';

type res = IsString<true> //No;
type res2 = IsString<'aaa'> //Yes;
Copy the code

This example uses both the Transformer API and the type checking API.

Let’s analyze the idea below:

Thought analysis

We parse the TS code into an AST, and then use the AST to find the node to convert, in this case the TypeReference node.

You can check it out at AstExplorer.net:

IsString is a TypeReference, which refers to another type, and then typeName is IsString and typeArguments are true.

Isn’t it a function call? That’s the essence of advanced typing, which is to find the final type by passing type arguments to the referenced advanced type.

And then once we find the TypeReference node we can use the Type Checker API to figure out the type value, and then create a comment node to add to it.

After converting the AST, print it out as a TS code string.

That’s the idea. Now let’s implement it and get familiar with the TS API.

Code implementation

Parse code into an AST requires specifying the file to compile and the compile parameters (createProgram API), and then getting the AST of the different files (getSourceFile API).

const ts = require("typescript");

const filename = "./input.ts";
const program = ts.createProgram([filename], {}); // The second parameter is compiler Options, as in the configuration file

const sourceFile = program.getSourceFile(filename);
Copy the code

The sourceFile here is the root of the AST.

We need to transform the AST using the transform API:

const  { transformed } = ts.transform(sourceFile, [
    function (context) {
        return function (node) { 
            return ts.visitNode(node, visit); 

            function visit(node) {
                if (ts.isTypeReferenceNode(node)) {
                   // ...
                }
                returnts.visitEachChild(node, visit, context) } }; }]);Copy the code

The Transform passes in the traversal AST and the transfomerFactory.

  • AST is the sourceFile from parse above.

  • The transformerFactory can be used with many apis in the context, and its return value is the transformer function.

The Transformer argument is node and the return value is the modified node.

To modify node, traverse node using the VISIT API and vistEachChild API, filtering out TypeReference nodes by type.

Then the TypeReference node is converted as follows:

if (ts.isTypeReferenceNode(node)) {
    const type = typeChecker.getTypeFromTypeNode(node);

    if(type.value){ ts.addSyntheticTrailingComment(node, ts.SyntaxKind.SingleLineCommentTrivia, type.value); }}Copy the code

Is to get through typeCheker IsString this type of final type value, and then through the API addSyntheticTrailingComment behind add a comment.

The typeChecker used here is retrieved from the getTypeChecker API:

const typeChecker = program.getTypeChecker();
Copy the code

This completes our purpose of transforming TS AST.

Then through printer, the AST is printed into TS code.

const printer =ts.createPrinter();

const code = printer.printNode(false, transformed[0], transformed[0]);

console.log(code);
Copy the code

That’s it. Let’s test it out.

Before the test, the entire code is put here:

const ts = require("typescript");

const filename = "./input.ts";
const program = ts.createProgram([filename], {}); // The second parameter is compiler Options, as in the configuration file

const sourceFile = program.getSourceFile(filename);

const typeChecker = program.getTypeChecker();

const  { transformed } = ts.transform(sourceFile, [
    function (context) {
        return function (node) { 
            return ts.visitNode(node, visit); 
            function visit(node) {
                if (ts.isTypeReferenceNode(node)) {
                    const type = typeChecker.getTypeFromTypeNode(node);

                    if(type.value){ ts.addSyntheticTrailingComment(node, ts.SyntaxKind.SingleLineCommentTrivia, type.value); }}returnts.visitEachChild(node, visit, context) } }; }]);const printer =ts.createPrinter();

const code = printer.printNode(false, transformed[0], transformed[0]);

console.log(code);
Copy the code

The test results

After testing, we achieved the goal of figuring out the type to add to the comment below

analyse

This is our first example of ts Transformer. Although simple, we also learned how to parse, transform, print, and Type check ts code.

Babel also has parse, transform and generate, but there is no type check process and it cannot be printed as TS code.

Using the Compiler API, you will find that advanced types are typeReference and need to be evaluated by passing in typeArguments, thus understanding advanced types better.

conclusion

Once you’re familiar with typescript syntax and configuration, you can learn about the COMPILER API to get into the TS compilation process. It includes apis such as Transfomer and Type Checker, which can transform TS code like the Babel plug-in, and also do type checking.

We used an example to familiarize ourselves with typescript compilation and transformer writing.

When you need to modify TS code to generate TS code, which Babel can’t do, it can only generate JS code, consider typescript’s custom Transformer.

And using the typescript Compiler API can deepen your understanding of the TS compilation process and type checking.

The TS Compiler API, especially the custom Transformer in it, is a good way to take typescript to the next level.