New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix TypeScript Enum self-references #14093
Changes from 4 commits
1326eaa
83c7fa0
40c50c2
933d994
741614d
2c8362f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
import assert from "assert"; | ||
import { template } from "@babel/core"; | ||
import type * as t from "@babel/types"; | ||
import type { NodePath } from "@babel/traverse"; | ||
import traverse from "@babel/traverse"; | ||
import type { NodePath, Visitor } from "@babel/traverse"; | ||
|
||
export default function transpileEnum(path, t) { | ||
const { node } = path; | ||
|
@@ -124,7 +125,29 @@ export function translateEnumValues( | |
value = t.stringLiteral(constValue); | ||
} | ||
} else { | ||
value = initializer; | ||
const IdentifierVisitor: Visitor = { | ||
Identifier(expr) { | ||
if (seen.has(expr.node.name)) { | ||
expr.replaceWith( | ||
t.memberExpression( | ||
t.cloneNode(path.node.id), | ||
t.cloneNode(expr.node), | ||
), | ||
); | ||
expr.skip(); | ||
} | ||
}, | ||
MemberExpression(expr) { | ||
expr.skip(); | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that skipping member expressions breaks transpilation of this code: enum A {
a = 0,
b = 1,
// @ts-ignore
x = a.toString(),
} You can try visiting |
||
}; | ||
|
||
const exprStmt = t.expressionStatement(initializer); | ||
|
||
traverse(t.program([exprStmt]), IdentifierVisitor); | ||
|
||
value = exprStmt.expression; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than creating a "fake" program in order to traverse this, you can:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, it helps a lot. I've run into another problem. |
||
seen.set(name, undefined); | ||
} | ||
} else if (typeof constValue === "number") { | ||
constValue += 1; | ||
|
@@ -140,6 +163,7 @@ export function translateEnumValues( | |
true, | ||
); | ||
value = t.binaryExpression("+", t.numericLiteral(1), lastRef); | ||
seen.set(name, undefined); | ||
} | ||
|
||
lastName = name; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
var x = 10; | ||
|
||
enum Foo { | ||
a = 10, | ||
b = a, | ||
c = b + x, | ||
} | ||
|
||
enum Bar { | ||
D = Foo.a, | ||
E = D, | ||
F = Math.E, | ||
G = E + Foo.c, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
var x = 10; | ||
var Foo; | ||
|
||
(function (Foo) { | ||
Foo[Foo["a"] = 10] = "a"; | ||
Foo[Foo["b"] = 10] = "b"; | ||
Foo[Foo["c"] = Foo.b + x] = "c"; | ||
})(Foo || (Foo = {})); | ||
|
||
var Bar; | ||
|
||
(function (Bar) { | ||
Bar[Bar["D"] = Foo.a] = "D"; | ||
Bar[Bar["E"] = Bar.D] = "E"; | ||
Bar[Bar["F"] = Math.E] = "F"; | ||
Bar[Bar["G"] = Bar.E + Foo.c] = "G"; | ||
})(Bar || (Bar = {})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move this visitor to the top-level of the file. We do some processing+validation to visitors, and if it's always the same object this work can be cached. You can get the
seen
map by using the "state" parameter:(style nit: we always call non-classes with a name that starts with a lowercase letter. Maybe
enumSelfReferenceVisitor
.