Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to hook up to the TypeScript compiler and emit some custom JavaScript code based on the TypeScript code model?

Tags:

typescript

I am wondering if I can somehow extend or hook up to the TypeScript compiler and generate some functions based on the declaration of the TypeScript interfaces in my code. Basically these functions would validate the data objects that are coming from the server to comply with the TypeScript interfaces the objects are supposed to implement. I was thinking to have a special interface-marker and I wish my hook to the compiler was triggered by such interface. Once that marker-interface is detected I would analyze the members of that interface and generate the code that would validate a given object against the data-contract defined by that interface. Is there a way to do that? Can anyone give some directions?

like image 858
Trident D'Gao Avatar asked Jan 16 '14 23:01

Trident D'Gao


People also ask

How does TypeScript compile to JavaScript?

The TypeScript compiler compiles these files and outputs the JavaScript with . js extension by keeping the same file name as the individual input file. The TypeScript compiler also preserves the original file path, hence the . js output file will be generated where the input file was in the directory structure.

Can I code JavaScript in TypeScript?

Any valid JavaScript is also valid TypeScript. This means that you can write literal JS in any place in your code.

Can you combine TypeScript and JavaScript?

The TypeScript compiler supports a mix of JavaScript and TypeScript files if we use the compiler option --allowJs : TypeScript files are compiled. JavaScript files are simply copied over to the output directory (after a few simple type checks).


2 Answers

Short answer

Yes (thanks to TypeScript transformers), and there already exists a tool like that called typescript-is.

Long answer

A transformer is a function for which the AST of your program is exposed. A basic example:

import * as ts from 'typescript';

export default (program: ts.Program) => {
    return (ctx: ts.TransformationContext) => {
        return (sourceFile: ts.SourceFile) => {
            function visitor(node: ts.Node): ts.Node {
                /**
                 * If that's the kind of node you were looking for,
                 * do something with it and return it. Otherwise:
                 */
                return ts.visitEachChild(node, visitor, ctx);
            }

            return ts.visitEachChild(sourceFile, visitor, ctx);
        };
    };
}

Such a transformer can be used programmatically. If you would rather make it a standalone plugin used in your tsconfig.json, you will need a wrapper for the TypeScript compiler that supports that, because TypeScript doesn't support plugins out of the box yet. ttypescript is an example of such a wrapper.

like image 190
Karol Majewski Avatar answered Jan 03 '23 16:01

Karol Majewski


This seems like an interesting idea. I think you'd probably end up needing to modify the compiler code to do this; standard "tsc" doesn't export type information, other than the --declaration command line option, which I don't think does what you want.

You might start by looking at some code that has done a programmatic integration with the TS compiler. For instance, the grunt type script module: https://github.com/k-maru/grunt-typescript/blob/master/src/modules/compiler.ts

Still, this seems like a non-trivial problem. One way is to create TS code that references one of your data objects and tries to compile it to see if that data conforms to an interface. If you end up having to use the compiler itself to do this validation, you would probably find that its too slow for real time use.

Or, if you generate code yourself based on your marker interfaces, you might end up reimplementing a lot of the compiler, since the type system can get pretty complex (generics, function declarations, and structural subobjects come to mind)

like image 25
mushin Avatar answered Jan 03 '23 17:01

mushin