Fir*_*ind 6 abstract-syntax-tree typescript typescript-compiler-api ts-morph
我正在尝试使用 Typescript API 从从其他来源导入类型的 Typescript 文件中获取信息。
我有常见类型:
// example.types.ts
export interface Example<T> {
description: string;
title: string;
element: T;
}
Run Code Online (Sandbox Code Playgroud)
然后我想为其准备一些元素组件或类
// Canvas.ts
interface ICanvasConfig {
name: string;
size: {
width: number;
height: number;
};
}
export const Canvas = (config: ICanvasConfig): void => {
console.log(config);
};
Run Code Online (Sandbox Code Playgroud)
这是我要解析的目标文件
// Canvas.example.ts
import type { Example } from './example.types';
import { Canvas } from './Canvas';
const exampleMeta: Example<typeof Canvas> = {
title: 'Canvas Element',
description: 'Element used for..',
element: Canvas
};
export default exampleMeta;
Run Code Online (Sandbox Code Playgroud)
我期待的是最后得到类似的东西
{
title: {
value: 'Canvas Element',
type: 'string'
}
description: {
value: 'Element used for..',
type: 'string',
},
element: {
value: Canvas, // real value
type: {
name: 'function',
args: [{
name: 'string',
size: {
width: 'number',
height: 'number'
}
}],
ret: 'void'
}
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试使用ts编译器,ts-morph但我所做的只是拐弯抹角。我不会发布我的解决方案,试图不分散您的注意力,但似乎我不了解 ts 节点的内部结构来获得我需要的东西。我玩的最多的就是检测标题、描述作为字符串,但any类型为元素。
所以我的问题是真的可以使用这些工具吗?如果是的话怎么办?有没有更好的解决方案来实现我的需求?
或者,如果您曾经遇到过类似的问题,分享您的经验将不胜感激。
您可以使用 ts-morph 和下面的代码来实现这一点。此代码仅适用于您提供的示例代码,但使用递归实现更通用的解决方案应该很容易。
我还发现了一个相关的 GitHub 问题“问题:是否有一种简单的方法可以将类型简化为仅基元?” 它有一个用于扩展类型的库https://github.com/dsherret/ts-morph/issues/1204#issuecomment-961546054,尽管它不提取“Canvas Element”等值。要使用该添加type exampleMeta = typeof exampleMeta并Canvas.example.ts运行import { typeFootprint } from "./typeFootprint"; console.log(typeFootprint("src/Canvas.example.ts", "exampleMeta"));以获取字符串 *1。
import { Project, SyntaxKind } from "ts-morph";
import util from "util" // for displaying a deep object
const project = new Project({})
// Assuming the files `example.types.ts`, `Canvas.ts`, and `Canvas.example.ts` are in `./src`.
project.addSourceFilesAtPaths("src/**/*.ts")
const exampleMeta = project.getSourceFile("src/Canvas.example.ts")!.getVariableDeclaration("exampleMeta")!
const exampleMetaValue = exampleMeta
// "go to definition"
.getFirstChildByKindOrThrow(SyntaxKind.Identifier)
.getDefinitionNodes()[0]!
// get the initializer `{ title: ..., description: ..., ... }`
.asKindOrThrow(SyntaxKind.VariableDeclaration)
.getInitializerOrThrow()
// "go to type definition"
const exampleMetaType = exampleMeta
.getType()
console.log(util.inspect({
// You can list properties with `exampleMetaType.getProperties().map((property) => property.getName())`
title: {
value: exampleMetaValue
// .title
.asKindOrThrow(SyntaxKind.ObjectLiteralExpression) // You can check the kind with `.isKind()` or `.getKindName()`
.getPropertyOrThrow("title")
// get the initializer `Create Element`
.asKindOrThrow(SyntaxKind.PropertyAssignment)
.getInitializerOrThrow()
.getText(),
type: exampleMetaType
// .title
.getPropertyOrThrow("title")
.getTypeAtLocation(exampleMeta)
.getText(),
},
description: {
value: exampleMetaValue
// .description
.asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
.getPropertyOrThrow("description")
// get the initializer `Element used for..`
.asKindOrThrow(SyntaxKind.PropertyAssignment)
.getInitializerOrThrow()
.getText(),
type: exampleMetaType
// .description
.getPropertyOrThrow("description")
.getTypeAtLocation(exampleMeta)
.getText(),
},
element: {
value: exampleMetaValue
// .element
.asKindOrThrow(SyntaxKind.ObjectLiteralExpression)
.getPropertyOrThrow("element")
// get the initializer `Canvas`
.asKindOrThrow(SyntaxKind.PropertyAssignment)
.getInitializerOrThrow()
.getText(),
type: {
name: exampleMetaType
// .element
.getPropertyOrThrow("element")
.getTypeAtLocation(exampleMeta)
.getCallSignatures().length > 0 ? "function" : "",
args: exampleMetaType
// .element
.getPropertyOrThrow("element")
.getTypeAtLocation(exampleMeta)
// Parse '(config: ICanvasConfig) => void'
.getCallSignatures()[0]!
.getParameters().map((arg) => {
const iCanvasPropertyType: any = {}
for (const iCanvasConfigProperty of arg.getTypeAtLocation(exampleMeta).getProperties()) {
iCanvasPropertyType[iCanvasConfigProperty.getName()] = iCanvasConfigProperty
.getTypeAtLocation(exampleMeta)
.getText() // TODO: Parse '{ width: number; height: number; }'
}
return iCanvasPropertyType
}),
ret: exampleMetaType
// .element
.getPropertyOrThrow("element")
.getTypeAtLocation(exampleMeta)
// Parse '(config: ICanvasConfig) => void'
.getCallSignatures()[0]!
.getReturnType()
.getText(), // .getFlags() & TypeFlags.Void === true
},
},
}, { depth: null }))
Run Code Online (Sandbox Code Playgroud)
*1
type exampleMeta = {
description: string;
title: string;
element(config: {
name: string;
size: {
width: number;
height: number;
};
}): void;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1001 次 |
| 最近记录: |