如何在打字稿中获得联合类型的完整 AST 表示?
How to get full AST representation of union type in typescript?
我知道 ts-ast-viewer,但我不知道他们如何从联合中提取元素列表。
我已经尝试了几种现有的解决方案,包括 ,但似乎大多数都已过时。一些 ts.[methods]
现在已弃用。
这是启动调试编译器的初始代码API:
import * as ts from "typescript";
const code = "type Foo = 'one'|'two'|'three'";
const sourceFile = ts.createSourceFile(
"foo.ts",
code,
ts.ScriptTarget.Latest,
true
);
function print(node: ts.Node, name: string) {
console.log({ node });
}
print(sourceFile, "Foo");
我知道 TS wiki 中的示例。这是我稍微修改过的版本:
//@ts-ignore
import * as ts from "typescript";
const code = "type Foo = 'one'|'two'|'three'";
const sourceFile = ts.createSourceFile(
"foo.ts",
code,
ts.ScriptTarget.Latest,
true
);
function extract(identifiers: string[]): void {
//@ts-ignore
const unfoundNodes = [];
//@ts-ignore
const foundNodes = [];
//@ts-ignore
ts.forEachChild(sourceFile, (node) => {
let name = "";
//@ts-ignore
if (ts.isFunctionDeclaration(node)) {
//@ts-ignore
name = node.name.text;
//@ts-ignore
node.body = undefined;
//@ts-ignore
} else if (ts.isVariableStatement(node)) {
//@ts-ignore
name = node.declarationList.declarations[0].name.getText(sourceFile);
//@ts-ignore
} else if (ts.isInterfaceDeclaration(node)) {
name = node.name.text;
}
//@ts-ignore
const container = identifiers.includes(name) ? foundNodes : unfoundNodes;
//@ts-ignore
container.push([name, node]);
});
//@ts-ignore
return (unfoundNodes[0][1] as any).type.types.map(
(elem: any) => elem.literal.text
);
}
// Run the extract function with the script's arguments
console.log(extract(["Foo"]));
并且有效。但是,如果我将源代码从 "type Foo = 'one'|'two'|'three'"
更改为 "type Foo = keyof Array<any>"
- 它不会。
我知道我的版本不对
此外,我试过 this 示例,但似乎 forEachDescendant
在 node
上不存在。
如何写一个函数来获取联合数组的元素?
预期函数:
import * as ts from "typescript";
const sourceCode = "type Foo = keyof Array<number>";
union(sourceCode, "Foo") // ["forEach", "reduce", "map" ...]
我需要这个用于调试目的。
您不能为此使用 AST,因为 AST 仅包含有关文本在文件中的外观的信息,而不包含代码的某些部分与其他部分的关系。为此,您需要使用类型检查器。这是一个独立的例子:
// available as @ts-morph/bootstrap on npm
import { ts, createProjectSync } from "https://deno.land/x/ts_morph@13.0.3/bootstrap/mod.ts";
// setup code... you can use the vanilla ts compiler for this, but it's a lot of work
const project = createProjectSync();
const sourceCode = "type Foo = keyof Array<number>";
const sourceFile = project.createSourceFile("file.ts", sourceCode);
const typeChecker = project.createProgram().getTypeChecker();
// example use with the compiler API
const fooTypeAlias = sourceFile.statements[0] as ts.TypeAliasDeclaration;
const fooType = typeChecker.getTypeAtLocation(fooTypeAlias.name);
if (fooType.isUnion()) {
for (const type of fooType.types) {
console.log(typeChecker.typeToString(type, fooTypeAlias.name));
}
}
输出:
number
"length"
"toString"
"toLocaleString"
"pop"
"push"
"concat"
"join"
"reverse"
"shift"
"slice"
"sort"
"splice"
"unshift"
"indexOf"
"lastIndexOf"
"every"
"some"
"forEach"
"map"
"filter"
"reduce"
"reduceRight"
我知道 ts-ast-viewer,但我不知道他们如何从联合中提取元素列表。
我已经尝试了几种现有的解决方案,包括 ts.[methods]
现在已弃用。
这是启动调试编译器的初始代码API:
import * as ts from "typescript";
const code = "type Foo = 'one'|'two'|'three'";
const sourceFile = ts.createSourceFile(
"foo.ts",
code,
ts.ScriptTarget.Latest,
true
);
function print(node: ts.Node, name: string) {
console.log({ node });
}
print(sourceFile, "Foo");
我知道 TS wiki 中的示例。这是我稍微修改过的版本:
//@ts-ignore
import * as ts from "typescript";
const code = "type Foo = 'one'|'two'|'three'";
const sourceFile = ts.createSourceFile(
"foo.ts",
code,
ts.ScriptTarget.Latest,
true
);
function extract(identifiers: string[]): void {
//@ts-ignore
const unfoundNodes = [];
//@ts-ignore
const foundNodes = [];
//@ts-ignore
ts.forEachChild(sourceFile, (node) => {
let name = "";
//@ts-ignore
if (ts.isFunctionDeclaration(node)) {
//@ts-ignore
name = node.name.text;
//@ts-ignore
node.body = undefined;
//@ts-ignore
} else if (ts.isVariableStatement(node)) {
//@ts-ignore
name = node.declarationList.declarations[0].name.getText(sourceFile);
//@ts-ignore
} else if (ts.isInterfaceDeclaration(node)) {
name = node.name.text;
}
//@ts-ignore
const container = identifiers.includes(name) ? foundNodes : unfoundNodes;
//@ts-ignore
container.push([name, node]);
});
//@ts-ignore
return (unfoundNodes[0][1] as any).type.types.map(
(elem: any) => elem.literal.text
);
}
// Run the extract function with the script's arguments
console.log(extract(["Foo"]));
并且有效。但是,如果我将源代码从 "type Foo = 'one'|'two'|'three'"
更改为 "type Foo = keyof Array<any>"
- 它不会。
我知道我的版本不对
此外,我试过 this 示例,但似乎 forEachDescendant
在 node
上不存在。
如何写一个函数来获取联合数组的元素?
预期函数:
import * as ts from "typescript";
const sourceCode = "type Foo = keyof Array<number>";
union(sourceCode, "Foo") // ["forEach", "reduce", "map" ...]
我需要这个用于调试目的。
您不能为此使用 AST,因为 AST 仅包含有关文本在文件中的外观的信息,而不包含代码的某些部分与其他部分的关系。为此,您需要使用类型检查器。这是一个独立的例子:
// available as @ts-morph/bootstrap on npm
import { ts, createProjectSync } from "https://deno.land/x/ts_morph@13.0.3/bootstrap/mod.ts";
// setup code... you can use the vanilla ts compiler for this, but it's a lot of work
const project = createProjectSync();
const sourceCode = "type Foo = keyof Array<number>";
const sourceFile = project.createSourceFile("file.ts", sourceCode);
const typeChecker = project.createProgram().getTypeChecker();
// example use with the compiler API
const fooTypeAlias = sourceFile.statements[0] as ts.TypeAliasDeclaration;
const fooType = typeChecker.getTypeAtLocation(fooTypeAlias.name);
if (fooType.isUnion()) {
for (const type of fooType.types) {
console.log(typeChecker.typeToString(type, fooTypeAlias.name));
}
}
输出:
number
"length"
"toString"
"toLocaleString"
"pop"
"push"
"concat"
"join"
"reverse"
"shift"
"slice"
"sort"
"splice"
"unshift"
"indexOf"
"lastIndexOf"
"every"
"some"
"forEach"
"map"
"filter"
"reduce"
"reduceRight"