TypeScript Compiler API 可以解析表达式的值吗?
Can TypeScript Compiler API resolve the value of an expression?
我想知道是否有一种方法可以解析表达式的最终值(使用 TypeScript 编译器 API),其工作方式类似于 getTypeAtLocation。
举个例子:
const g = {
a: 'hello'
}
export enum wonkyEnum {
A, // = 0
B = 1,
C = g['a'] as any,
D = "str" as any
E = [ ...[1,2,3] ][2]
}
- 在此示例中,C、D 和 E 将无法与 getConstantValue() 一起使用。
- D 很容易解析,但是,C 和 E 是由于使用的表达式、引用等原因导致的更深层次问题的示例.
我希望甚至有一个部分解决方案不需要编写涉及的递归解析器,它涵盖了初始化表达式的所有可能的意外情况。如果在 TS 中没有暴露任何内容,第 3 方开源代码也可能有用。
否则,任何能帮助我了解这个地方所有可能的节点变化的细节都会有所帮助!
我已经有了一个健壮的递归解析器系统,我主要使用它来遍历 Type 结构,但它也可以为 Node / Kind 实现。我最想确定的是,在走那条路之前,我不会不必要地弄乱代码或重新发明轮子。
谢谢!
编辑
David 在下面的回答是正确的 - 没有办法用 TS 做到这一点。
在进一步探索时,为了涵盖所有情况,需要进行实际评估。这可能会变得混乱,在许多情况下是不可能的(见下面的评论)。
幸运的是,对于枚举,TypeScript 似乎通常能够使用 getConstantValue() 方法获得答案。
作为回退,我们将递归解析初始化器节点,涵盖一些表达式情况(如下所示):
// key is tested for node.kind equality in parseInitializer()
const valueParsers = Map<number | number[], (node: any) => string | number | boolean | null>([
[SyntaxKind.TrueKeyword, () => true],
[SyntaxKind.FalseKeyword, () => false],
[SyntaxKind.NullKeyword, () => null],
[SyntaxKind.StringLiteral, (node: LiteralLikeNode) => node.text],
[
[SyntaxKind.AsExpression, SyntaxKind.ParenthesizedExpression, SyntaxKind.TypeAssertionExpression],
(node: any) => parseInitializer(node.expression) // recursive call
],
]);
如果有人有任何关于扩大它的想法又不会太疯狂,请随时在下面发表评论!
这样做很有用,但是编译器没有内置任何东西来解析表达式值。它与此无关...类型检查器上的 getConstantValue
专门针对枚举成员。
我也不相信目前有任何图书馆可以帮助做到这一点……至少我没见过。也许您或其他人想创建一个?那将是一个有趣的项目!
无论如何,这些只是我的高层次想法,可以工作,但可以改进:
查找 属性 分配值
这样做有点困难,因为在将某些内容分配给枚举成员之前,您必须检查是否修改了 g.a
:
const g = { a: 'hello' };
g.a = 'other string';
如果你真的想实现类似的东西,那么我相信一种方法是获取 g.a
(getSymbolAtLocation
) 的符号,然后从那里查看 属性 中的赋值 #valueDeclaration
属性。从 属性 赋值开始,您必须向下遍历树中的节点,以便在枚举成员声明之前获得对 属性 的最后赋值(有些自找麻烦的人可能会重新分配 属性 在之前的枚举成员初始值设定项中)。如果赋值表达式是常量或您可以解析的某些表达式,那么您就知道该值。如果不是,那么您将不得不转到该声明并从那里遍历树。基本上,它开始变得非常复杂,您将无法始终静态地确定该值。
也许您的分析器的限制可能是不允许使用字符串类型,在这种情况下应该使用 const 断言?
const g = { a: 'hello' } as const;
在那种情况下,getTypeAtLocation
for a
in g.a
是 "hello"
字符串文字类型。
嵌套数组
对于这些嵌套数组 [ ...[1,2,3] ][2]
,您可以按 post 顺序遍历树,解析数组项,由于扩展语法而展平数组,然后获取数组中的第三项。也许知道它只是在寻找第三项,您可以优化它并且只解决足够的问题以获得第二项(如果数组类似于 [...[g.a, 2, 3]]
,这可能会节省大量时间)。
我想知道是否有一种方法可以解析表达式的最终值(使用 TypeScript 编译器 API),其工作方式类似于 getTypeAtLocation。
举个例子:
const g = {
a: 'hello'
}
export enum wonkyEnum {
A, // = 0
B = 1,
C = g['a'] as any,
D = "str" as any
E = [ ...[1,2,3] ][2]
}
- 在此示例中,C、D 和 E 将无法与 getConstantValue() 一起使用。
- D 很容易解析,但是,C 和 E 是由于使用的表达式、引用等原因导致的更深层次问题的示例.
我希望甚至有一个部分解决方案不需要编写涉及的递归解析器,它涵盖了初始化表达式的所有可能的意外情况。如果在 TS 中没有暴露任何内容,第 3 方开源代码也可能有用。
否则,任何能帮助我了解这个地方所有可能的节点变化的细节都会有所帮助!
我已经有了一个健壮的递归解析器系统,我主要使用它来遍历 Type 结构,但它也可以为 Node / Kind 实现。我最想确定的是,在走那条路之前,我不会不必要地弄乱代码或重新发明轮子。
谢谢!
编辑
David 在下面的回答是正确的 - 没有办法用 TS 做到这一点。
在进一步探索时,为了涵盖所有情况,需要进行实际评估。这可能会变得混乱,在许多情况下是不可能的(见下面的评论)。
幸运的是,对于枚举,TypeScript 似乎通常能够使用 getConstantValue() 方法获得答案。
作为回退,我们将递归解析初始化器节点,涵盖一些表达式情况(如下所示):
// key is tested for node.kind equality in parseInitializer()
const valueParsers = Map<number | number[], (node: any) => string | number | boolean | null>([
[SyntaxKind.TrueKeyword, () => true],
[SyntaxKind.FalseKeyword, () => false],
[SyntaxKind.NullKeyword, () => null],
[SyntaxKind.StringLiteral, (node: LiteralLikeNode) => node.text],
[
[SyntaxKind.AsExpression, SyntaxKind.ParenthesizedExpression, SyntaxKind.TypeAssertionExpression],
(node: any) => parseInitializer(node.expression) // recursive call
],
]);
如果有人有任何关于扩大它的想法又不会太疯狂,请随时在下面发表评论!
这样做很有用,但是编译器没有内置任何东西来解析表达式值。它与此无关...类型检查器上的 getConstantValue
专门针对枚举成员。
我也不相信目前有任何图书馆可以帮助做到这一点……至少我没见过。也许您或其他人想创建一个?那将是一个有趣的项目!
无论如何,这些只是我的高层次想法,可以工作,但可以改进:
查找 属性 分配值
这样做有点困难,因为在将某些内容分配给枚举成员之前,您必须检查是否修改了 g.a
:
const g = { a: 'hello' };
g.a = 'other string';
如果你真的想实现类似的东西,那么我相信一种方法是获取 g.a
(getSymbolAtLocation
) 的符号,然后从那里查看 属性 中的赋值 #valueDeclaration
属性。从 属性 赋值开始,您必须向下遍历树中的节点,以便在枚举成员声明之前获得对 属性 的最后赋值(有些自找麻烦的人可能会重新分配 属性 在之前的枚举成员初始值设定项中)。如果赋值表达式是常量或您可以解析的某些表达式,那么您就知道该值。如果不是,那么您将不得不转到该声明并从那里遍历树。基本上,它开始变得非常复杂,您将无法始终静态地确定该值。
也许您的分析器的限制可能是不允许使用字符串类型,在这种情况下应该使用 const 断言?
const g = { a: 'hello' } as const;
在那种情况下,getTypeAtLocation
for a
in g.a
是 "hello"
字符串文字类型。
嵌套数组
对于这些嵌套数组 [ ...[1,2,3] ][2]
,您可以按 post 顺序遍历树,解析数组项,由于扩展语法而展平数组,然后获取数组中的第三项。也许知道它只是在寻找第三项,您可以优化它并且只解决足够的问题以获得第二项(如果数组类似于 [...[g.a, 2, 3]]
,这可能会节省大量时间)。