我正在使用TypeScript编译器API来解析一些源代码并获取类引用。
与类定义对应的Node
对象有一个decorators
数组,但我找不到获取每个装饰器名称的方法。
我使用了TypeScript wiki中的this example
///<reference path="typings/node/node.d.ts" />
import * as ts from "typescript";
import * as fs from "fs";
interface DocEntry {
name?: string,
fileName?: string,
documentation?: string,
type?: string,
constructors?: DocEntry[],
parameters?: DocEntry[],
returnType?: string
};
/** Generate documention for all classes in a set of .ts files */
function generateDocumentation(fileNames: string[], options: ts.CompilerOptions): void {
// Build a program using the set of root file names in fileNames
let program = ts.createProgram(fileNames, options);
// Get the checker, we will use it to find more about classes
let checker = program.getTypeChecker();
let output: DocEntry[] = [];
// Visit every sourceFile in the program
for (const sourceFile of program.getSourceFiles()) {
// Walk the tree to search for classes
ts.forEachChild(sourceFile, visit);
}
// print out the doc
fs.writeFileSync("classes.json", JSON.stringify(output, undefined, 4));
return;
/** visit nodes finding exported classes */
function visit(node: ts.Node) {
// Only consider exported nodes
if (!isNodeExported(node)) {
return;
}
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
// This is a top level class, get its symbol
let symbol = checker.getSymbolAtLocation((<ts.ClassDeclaration>node).name);
output.push(serializeClass(symbol));
// No need to walk any further, class expressions/inner declarations
// cannot be exported
}
else if (node.kind === ts.SyntaxKind.ModuleDeclaration) {
// This is a namespace, visit its children
ts.forEachChild(node, visit);
}
}
/** Serialize a symbol into a json object */
function serializeSymbol(symbol: ts.Symbol): DocEntry {
return {
name: symbol.getName(),
documentation: ts.displayPartsToString(symbol.getDocumentationComment()),
type: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration))
};
}
/** Serialize a class symbol infomration */
function serializeClass(symbol: ts.Symbol) {
let details = serializeSymbol(symbol);
// Get the construct signatures
let constructorType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
details.constructors = constructorType.getConstructSignatures().map(serializeSignature);
return details;
}
/** Serialize a signature (call or construct) */
function serializeSignature(signature: ts.Signature) {
return {
parameters: signature.parameters.map(serializeSymbol),
returnType: checker.typeToString(signature.getReturnType()),
documentation: ts.displayPartsToString(signature.getDocumentationComment())
};
}
/** True if this is visible outside this file, false otherwise */
function isNodeExported(node: ts.Node): boolean {
return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
}
}
generateDocumentation(process.argv.slice(2), {
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
});
答案 0 :(得分:4)
你可以在装饰器表达式中获得第一个标记,这将是函数声明。从函数中,您可以获得名称,参数,文档字符串等任何信息。
我有一个扩展的例子:
///<reference path="typings/node/node.d.ts" />
import * as ts from "typescript";
import * as fs from "fs";
interface DocEntry {
name?: string,
fileName?: string,
documentation?: string,
type?: string,
constructors?: DocEntry[],
parameters?: DocEntry[],
decorators?: DocEntry[],
returnType?: string
};
/** Generate documention for all classes in a set of .ts files */
function generateDocumentation(fileNames: string[], options: ts.CompilerOptions): void {
// Build a program using the set of root file names in fileNames
let program = ts.createProgram(fileNames, options);
// Get the checker, we will use it to find more about classes
let checker = program.getTypeChecker();
let output: DocEntry[] = [];
// Visit every sourceFile in the program
for (const sourceFile of program.getSourceFiles()) {
// Walk the tree to search for classes
ts.forEachChild(sourceFile, visit);
}
// print out the doc
fs.writeFileSync("classes.json", JSON.stringify(output, undefined, 4));
return;
/** visit nodes finding exported classes */
function visit(node: ts.Node) {
// Only consider exported nodes
if (!isNodeExported(node)) {
return;
}
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
// This is a top level class, get its symbol
output.push(serializeClass((<ts.ClassDeclaration>node)));
// No need to walk any further, class expressions/inner declarations
// cannot be exported
}
else if (node.kind === ts.SyntaxKind.ModuleDeclaration) {
// This is a namespace, visit its children
ts.forEachChild(node, visit);
}
}
/** Serialize a symbol into a json object */
function serializeSymbol(symbol: ts.Symbol): DocEntry {
return {
name: symbol.getName(),
documentation: ts.displayPartsToString(symbol.getDocumentationComment()),
type: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration))
};
}
/** Serialize a class symbol infomration */
function serializeClass(node: ts.ClassDeclaration) {
let symbol = checker.getSymbolAtLocation(node.name);
let details = serializeSymbol(symbol);
// Get the construct signatures
details.decorators = node.decorators.map(serializeDecorator);
let constructorType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
details.constructors = constructorType.getConstructSignatures().map(serializeSignature);
return details;
}
function serializeDecorator(decorator: ts.Decorator) {
let symbol = checker.getSymbolAtLocation(decorator.expression.getFirstToken());
let decoratorType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
let details = serializeSymbol(symbol);
details.constructors = decoratorType.getCallSignatures().map(serializeSignature);
return details;
}
/** Serialize a signature (call or construct) */
function serializeSignature(signature: ts.Signature) {
return {
parameters: signature.parameters.map(serializeSymbol),
returnType: checker.typeToString(signature.getReturnType()),
documentation: ts.displayPartsToString(signature.getDocumentationComment())
};
}
/** True if this is visible outside this file, false otherwise */
function isNodeExported(node: ts.Node): boolean {
return (node.flags & ts.NodeFlags.Export) !== 0 || (node.parent && node.parent.kind === ts.SyntaxKind.SourceFile);
}
}
generateDocumentation(process.argv.slice(2), {
target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS
});
现在有这样的示例代码:
function MyDecorator(myParam: string) {
}
@MyDecorator("myVal")
class MyTestClass {
}
我得到了输出:
[
{
"name": "MyTestClass",
"documentation": "",
"type": "typeof MyTestClass",
"decorators": [
{
"name": "MyDecorator",
"documentation": "",
"type": "(myParam: string) => void",
"constructors": [
{
"parameters": [
{
"name": "myParam",
"documentation": "",
"type": "string"
}
],
"returnType": "void",
"documentation": ""
}
]
}
],
"constructors": [
{
"parameters": [],
"returnType": "MyTestClass",
"documentation": ""
}
]
}
]