Typescript AST:如何获取断言类型?
Typescript AST : How to get the asserted type?
我正在尝试解析 class 并创建声明中没有值的 属性 声明的默认值。
例如来自这个:
class MyClass {
name = 'My Class';
id: string;
}
functionCall<MyClass>() // <-- I get the hold of the type class here
我想生成这样的东西:
{
name: 'My Class',
id: 'I am generated'
}
我从 functionCall<MyClass>
得到 MyClass
作为 type: ts.InterfaceType
节点,然后从那里解析属性:
const typeChecker = program.getTypeChecker();
const type = typeChecker.getTypeAtLocation(callExpression); // This is the node where `functioCall<MyClass>()` is
type.getProperties().forEach(symbol => {
// I am stuck here
});
我遇到的问题是,有些 symbols
有 symbol.valueDeclaration
未定义,我不知道用什么来知道
- 属性
的类型
- 如果作业完成。
虽然我检查 symbol.valueDeclaration
是否存在以及它们的 symbol.valueDeclaration.kind
是否是某个关键字(如 SyntaxKind.StringKeyword
),但它不适用于
class SomeClass {
noKeyword; // probably any?
}
如有任何帮助或指点,我们将不胜感激。
这是从脚本中获取 运行 时间类型作为对象:
/*
Used to handle types from:
InterfaceDeclaration,
ClassDeclaration,
TypeLiteral
*/
function getTypeDescriptor(
property: ts.PropertyDeclaration | ts.PropertySignature,
typeChecker: ts.TypeChecker,
): ts.Expression {
const { type } = property;
if (type !== undefined) {
if (property.initializer) {
switch (property.initializer.kind) {
case ts.SyntaxKind.StringLiteral:
return ts.createLiteral('string');
case ts.SyntaxKind.FirstLiteralToken:
return ts.createLiteral('number');
}
}
return ts.createLiteral('any');
}
return getDescriptor(type, typeChecker);
}
// Create expression based on objects or property types
export function getDescriptor(type: ts.Node | undefined, typeChecker: ts.TypeChecker): ts.Expression {
if (!type) {
return ts.createLiteral('unknown');
}
switch (type.kind) {
case ts.SyntaxKind.PropertySignature:
return getDescriptor((type as ts.PropertySignature).type, typeChecker);
case ts.SyntaxKind.StringKeyword:
return ts.createLiteral('string');
case ts.SyntaxKind.NumberKeyword:
return ts.createLiteral('number');
case ts.SyntaxKind.BooleanKeyword:
return ts.createLiteral('boolean');
case ts.SyntaxKind.AnyKeyword:
return ts.createLiteral('any');
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.TypeReference:
const symbol = typeChecker.getSymbolAtLocation((type as ts.TypeReferenceNode).typeName);
const declaration = ((symbol && symbol.declarations) || [])[0];
return getDescriptor(declaration, typeChecker);
case ts.SyntaxKind.ArrayType:
return ts.createArrayLiteral([getDescriptor((type as ts.ArrayTypeNode).elementType, typeChecker)]);
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.TypeLiteral:
return ts.createObjectLiteral(
(type as ts.TypeLiteralNode).members.map(member =>
ts.createPropertyAssignment(
member.name || '',
getTypeDescriptor(member as ts.PropertySignature, typeChecker),
),
),
);
default:
throw new Error('Unknown type ' + ts.SyntaxKind[type.kind]);
}
}
例子
有了这个class:
class X {
public id: number = 1;
}
属性 初始值设定项如下所示:
<ref *2> TokenObject {
parent: <ref *1> NodeObject {
parent: NodeObject {
parent: [SourceFileObject],
kind: SyntaxKind.ClassDeclaration,
name: [IdentifierObject],
typeParameters: undefined,
heritageClauses: undefined,
members: [Array],
symbol: [SymbolObject]
},
kind: SyntaxKind.PropertyDeclaration,
decorators: undefined,
modifiers: [ [TokenObject], pos: 154, end: 162, transformFlags: 536870913 ],
name: IdentifierObject {},
questionToken: undefined,
type: TokenObject {
kind: SyntaxKind.NumberKeyword
},
initializer: [Circular *2],
symbol: SymbolObject {
escapedName: 'id',
declarations: [Array],
valueDeclaration: [Circular *1],
parent: [SymbolObject],
}
},
kind: SyntaxKind.NumericLiteral,
text: '1',
}
text: '1'
是 属性 的值,kind: SyntaxKind.NumericLiteral
是 text
的类型,parent.type.kind: SyntaxKind.NumberKeyword
是 属性.
P.S.: 我知道的不多,但希望对你有所帮助
编辑:添加我如何使用转换器获取类型:
export function transformer(program: ts.Program, opts?: TransformerOptions) {
function visitor(ctx: ts.TransformationContext, sourcefile: ts.SourceFile, result: { seen: boolean }) {
const typeChecker = program.getTypeChecker();
const _visitor: ts.Visitor = (node: ts.Node) => {
if (ts.isCallExpression(node) && node.typeArguments && node.expression.getText(sourcefile) == 'generateRtti') {
const [type] = node.typeArguments;
const [argument] = node.arguments;
const fn = ts.createIdentifier(nameOfGenerateFunction);
const typeName = type.getText();
const typeSource = getDescriptor(type, typeChecker);
result.seen = true;
return ts.createCall(fn, undefined, [argument || ts.createStringLiteral(typeName), typeSource]);
}
return ts.visitEachChild(node, _visitor, ctx);
};
return _visitor;
}
return (ctx: ts.TransformationContext) => {
return (sourcefile: ts.SourceFile) => {
const result = { seen: false };
const newSourcefile = ts.visitNode(sourcefile, visitor(ctx, sourcefile, result));
if (result.seen) {
const generated_function = createGenerateFunction();
const statements: Array<ts.Statement> = [generated_function];
for (const statement of newSourcefile.statements) {
if (ts.isImportDeclaration(statement))
statements.splice(statements.length - 1, 0, statement);
else
statements.push(statement);
}
return ts.updateSourceFileNode(newSourcefile, statements);
}
return newSourcefile;
};
};
}
我快到了,我需要检查 valueDeclaration
的 children 中是否定义了文字
const propertyDeclaration = symbol.valueDeclaration;
if (ts.isPropertyDeclaration(propertyDeclaration)) {
const children = propertyDeclaration.getChildren();
const assignationLiteral = children.find(child => {
ts.isLiteralTypeNode;
return [
ts.SyntaxKind.NumericLiteral,
ts.SyntaxKind.StringLiteral,
ts.SyntaxKind.ObjectLiteralExpression,
].includes(child.kind);
});
if (assignationLiteral) {
// A value es defined!
} {
// I have a keyword, I have to check which one is it :)
}
}
我正在尝试解析 class 并创建声明中没有值的 属性 声明的默认值。
例如来自这个:
class MyClass {
name = 'My Class';
id: string;
}
functionCall<MyClass>() // <-- I get the hold of the type class here
我想生成这样的东西:
{
name: 'My Class',
id: 'I am generated'
}
我从 functionCall<MyClass>
得到 MyClass
作为 type: ts.InterfaceType
节点,然后从那里解析属性:
const typeChecker = program.getTypeChecker();
const type = typeChecker.getTypeAtLocation(callExpression); // This is the node where `functioCall<MyClass>()` is
type.getProperties().forEach(symbol => {
// I am stuck here
});
我遇到的问题是,有些 symbols
有 symbol.valueDeclaration
未定义,我不知道用什么来知道
- 属性 的类型
- 如果作业完成。
虽然我检查 symbol.valueDeclaration
是否存在以及它们的 symbol.valueDeclaration.kind
是否是某个关键字(如 SyntaxKind.StringKeyword
),但它不适用于
class SomeClass {
noKeyword; // probably any?
}
如有任何帮助或指点,我们将不胜感激。
这是从脚本中获取 运行 时间类型作为对象:
/*
Used to handle types from:
InterfaceDeclaration,
ClassDeclaration,
TypeLiteral
*/
function getTypeDescriptor(
property: ts.PropertyDeclaration | ts.PropertySignature,
typeChecker: ts.TypeChecker,
): ts.Expression {
const { type } = property;
if (type !== undefined) {
if (property.initializer) {
switch (property.initializer.kind) {
case ts.SyntaxKind.StringLiteral:
return ts.createLiteral('string');
case ts.SyntaxKind.FirstLiteralToken:
return ts.createLiteral('number');
}
}
return ts.createLiteral('any');
}
return getDescriptor(type, typeChecker);
}
// Create expression based on objects or property types
export function getDescriptor(type: ts.Node | undefined, typeChecker: ts.TypeChecker): ts.Expression {
if (!type) {
return ts.createLiteral('unknown');
}
switch (type.kind) {
case ts.SyntaxKind.PropertySignature:
return getDescriptor((type as ts.PropertySignature).type, typeChecker);
case ts.SyntaxKind.StringKeyword:
return ts.createLiteral('string');
case ts.SyntaxKind.NumberKeyword:
return ts.createLiteral('number');
case ts.SyntaxKind.BooleanKeyword:
return ts.createLiteral('boolean');
case ts.SyntaxKind.AnyKeyword:
return ts.createLiteral('any');
case ts.SyntaxKind.TypeAliasDeclaration:
case ts.SyntaxKind.TypeReference:
const symbol = typeChecker.getSymbolAtLocation((type as ts.TypeReferenceNode).typeName);
const declaration = ((symbol && symbol.declarations) || [])[0];
return getDescriptor(declaration, typeChecker);
case ts.SyntaxKind.ArrayType:
return ts.createArrayLiteral([getDescriptor((type as ts.ArrayTypeNode).elementType, typeChecker)]);
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.TypeLiteral:
return ts.createObjectLiteral(
(type as ts.TypeLiteralNode).members.map(member =>
ts.createPropertyAssignment(
member.name || '',
getTypeDescriptor(member as ts.PropertySignature, typeChecker),
),
),
);
default:
throw new Error('Unknown type ' + ts.SyntaxKind[type.kind]);
}
}
例子
有了这个class:
class X {
public id: number = 1;
}
属性 初始值设定项如下所示:
<ref *2> TokenObject {
parent: <ref *1> NodeObject {
parent: NodeObject {
parent: [SourceFileObject],
kind: SyntaxKind.ClassDeclaration,
name: [IdentifierObject],
typeParameters: undefined,
heritageClauses: undefined,
members: [Array],
symbol: [SymbolObject]
},
kind: SyntaxKind.PropertyDeclaration,
decorators: undefined,
modifiers: [ [TokenObject], pos: 154, end: 162, transformFlags: 536870913 ],
name: IdentifierObject {},
questionToken: undefined,
type: TokenObject {
kind: SyntaxKind.NumberKeyword
},
initializer: [Circular *2],
symbol: SymbolObject {
escapedName: 'id',
declarations: [Array],
valueDeclaration: [Circular *1],
parent: [SymbolObject],
}
},
kind: SyntaxKind.NumericLiteral,
text: '1',
}
text: '1'
是 属性 的值,kind: SyntaxKind.NumericLiteral
是 text
的类型,parent.type.kind: SyntaxKind.NumberKeyword
是 属性.
P.S.: 我知道的不多,但希望对你有所帮助
编辑:添加我如何使用转换器获取类型:
export function transformer(program: ts.Program, opts?: TransformerOptions) {
function visitor(ctx: ts.TransformationContext, sourcefile: ts.SourceFile, result: { seen: boolean }) {
const typeChecker = program.getTypeChecker();
const _visitor: ts.Visitor = (node: ts.Node) => {
if (ts.isCallExpression(node) && node.typeArguments && node.expression.getText(sourcefile) == 'generateRtti') {
const [type] = node.typeArguments;
const [argument] = node.arguments;
const fn = ts.createIdentifier(nameOfGenerateFunction);
const typeName = type.getText();
const typeSource = getDescriptor(type, typeChecker);
result.seen = true;
return ts.createCall(fn, undefined, [argument || ts.createStringLiteral(typeName), typeSource]);
}
return ts.visitEachChild(node, _visitor, ctx);
};
return _visitor;
}
return (ctx: ts.TransformationContext) => {
return (sourcefile: ts.SourceFile) => {
const result = { seen: false };
const newSourcefile = ts.visitNode(sourcefile, visitor(ctx, sourcefile, result));
if (result.seen) {
const generated_function = createGenerateFunction();
const statements: Array<ts.Statement> = [generated_function];
for (const statement of newSourcefile.statements) {
if (ts.isImportDeclaration(statement))
statements.splice(statements.length - 1, 0, statement);
else
statements.push(statement);
}
return ts.updateSourceFileNode(newSourcefile, statements);
}
return newSourcefile;
};
};
}
我快到了,我需要检查 valueDeclaration
的 children 中是否定义了文字
const propertyDeclaration = symbol.valueDeclaration;
if (ts.isPropertyDeclaration(propertyDeclaration)) {
const children = propertyDeclaration.getChildren();
const assignationLiteral = children.find(child => {
ts.isLiteralTypeNode;
return [
ts.SyntaxKind.NumericLiteral,
ts.SyntaxKind.StringLiteral,
ts.SyntaxKind.ObjectLiteralExpression,
].includes(child.kind);
});
if (assignationLiteral) {
// A value es defined!
} {
// I have a keyword, I have to check which one is it :)
}
}