Creating Typescript with the Typescript compiler
The following are things I found out by trial and error about typescript generation, mostly about creating classes
Before you begin
First and foremost don’t forget to import the compiler API.
import * as ts from ‘typescript’;
Its common for a function to make many arguments, even if it doesn’t use them all, use an IDE like Webstorm or VSCode to see what they are.
Figuring it out yourself
If you know how to write it in code, but can’t figure out how to compose it with the API you can always reverse engineer it by printing out the JSON of the AST.
function printTypescript(text: string) {
let sourceFile = ts.createSourceFile(
'afilename.ts', text,
ts.ScriptTarget.ES2015,
false,
);
console.log(JSON.stringify(sourceFile.statements, null, '\t'));
}
printTypescript(`export class Icecream {flavor: string;}`)
I used this to figure out how to add an export
statement to a class declaration. The code above produced the output below. The kind
property will tell you exactly what token it is. All kind
definitions can be found here: https://github.com/Microsoft/TypeScript/blob/8ddb2b61d2c996ab442b282ab9b36195ed697c13/lib/typescript.d.ts#L78
[[
{
"pos": 0,
"end": 50,
"flags": 0,
"kind": 240,
"modifiers": [
{
"pos": 0,
"end": 9,
"flags": 0,
"kind": 85
}
],
"name": {
"pos": 15,
"end": 24,
"flags": 0,
"escapedText": "Icecream"
},
"members": [
{
"pos": 26,
"end": 46,
"flags": 0,
"kind": 154,
"name": {
"pos": 26,
"end": 37,
"flags": 0,
"escapedText": "flavor"
},
"type": {
"pos": 38,
"end": 45,
"flags": 0,
"kind": 138
},
"modifierFlagsCache": 536870912
}
],
"modifierFlagsCache": 536870913
}
]
You can make it say export class foo {}
by using the export key as a modifier.
Printing the finished (or unfinished result)
After you’re done assembling your nodes you can turn them into a string. You can print anything, a single lonely keyword, a half-finished function or an entire class.
function nodeText(n: ts.Node): string {
const resultFile = ts.createSourceFile(
'test.ts',
'',
ts.ScriptTarget.Latest,
false,
ts.ScriptKind.TS,
);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
});
const result = printer.printNode(
ts.EmitHint.Unspecified,
n,
resultFile,
);
return result;
}
Creating a Class
// output class foo {}const classNode = ts.createClassExpression(undefined, 'foo', undefined, undefined, undefined);
Creating a class which inherits another
// output class foo extends BaseClass {}const thingImExtending = ts.createIdentifier('BaseClass');const thingImExtendingAsAnExpression = ts.createExpressionWithTypeArguments(undefined, thingImExtending);const heritageClause = ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [thingImExtendingAsAnExpression]);const classDec = ts.createClassDeclaration(undefined, undefined, 'foo', undefined, [heritageClause], undefined);
Creating a constructor with a parameter, question mark, and a type argument.
/* output
constructor(data?: Partial<foo>) {
super(data);
}
*/const partialType = ts.createTypeReferenceNode(‘Partial’, [ts.createTypeReferenceNode('foo', undefined)]);const dataId = ts.createIdentifier(‘data’);const param = ts.createParameter(undefined, undefined, undefined, dataId, ts.createToken(ts.SyntaxKind.QuestionToken), partialType);const sup = ts.createSuper();const superCall = ts.createCall(sup, undefined, [dataId]);const statement = ts.createExpressionStatement(superCall);const block = ts.createBlock([statement], true);const cons = ts.createConstructor(undefined, undefined, [param], block);const classDec = ts.createClassDeclaration(undefined, undefined, 'foo', undefined, undefined, [cons]);
Properties
/* output
class foo {
propertyName: Number;
}
*/const ref = ts.createTypeReferenceNode(‘Number’, []);const prop = ts.createProperty(undefined, undefined, ‘propertyName’, undefined, ref, undefined);const classDec = ts.createClassDeclaration(undefined, undefined, 'foo', undefined, undefined, [prop]);
Decorators
Decorators are particularly easy as they’re just function calls, this one has a parameter too;
/* output
@niceDecorator(“bar”)
class foo {
}
*/const p = ts.createIdentifier(‘niceDecorator’);
const call = ts.createCall(p, undefined, [str]);
const dec = ts.createDecorator(call);
const classDec = ts.createClassDeclaration([dec], undefined, ‘foo’, undefined, undefined, undefined);
Object Literals
An object literal is created out of an array of PropertyAssignment
/* output
{"keyFoo": "foo", "keyBar": 1}
*/const assignments: ts.PropertyAssignment[] = [];const key1 = "keyFoo";
const key2 = "keyBar";
const value1 = ts.createLiteral("foo");
const value2 = ts.createLiteral(1);
assignments.push(ts.createPropertyAssignment(key1, value1));
assignments.push(ts.createPropertyAssignment(key2, value2));const obj = ts.createObjectLiteral(assignments);