Skip to content

Commit 11e7b4f

Browse files
committed
Add support for @enum to for all variables with string literal properties
Resolves #1740.
1 parent c2f9a89 commit 11e7b4f

File tree

4 files changed

+52
-29
lines changed

4 files changed

+52
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Features
44

55
- Added support for displaying identifiers & property access expressions in initializers, #1730.
6+
- Expanded support for variables tagged with `@enum` to all variables whose property types are string literals, #1740.
67

78
### Bug Fixes
89

src/lib/converter/symbols.ts

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ function convertVariable(
818818
const type = context.checker.getTypeOfSymbolAtLocation(symbol, declaration);
819819

820820
if (
821-
isEnumLiteral(declaration as ts.VariableDeclaration) &&
821+
isEnumLike(context.checker, type, declaration) &&
822822
symbol.getJsDocTags().some((tag) => tag.name === "enum")
823823
) {
824824
return convertVariableAsEnum(context, symbol, exportSymbol);
@@ -852,27 +852,15 @@ function convertVariable(
852852
context.finalizeDeclarationReflection(reflection, symbol, exportSymbol);
853853
}
854854

855-
function isEnumLiteral(declaration: ts.VariableDeclaration) {
856-
if (
857-
!hasAllFlags(declaration.parent.flags, ts.NodeFlags.Const) ||
858-
!declaration.initializer ||
859-
!ts.isAsExpression(declaration.initializer) ||
860-
declaration.initializer.type.getText() !== "const"
861-
) {
862-
// Not an as-const expression
863-
return false;
864-
}
865-
866-
const body = declaration.initializer.expression;
867-
if (!ts.isObjectLiteralExpression(body)) {
855+
function isEnumLike(checker: ts.TypeChecker, type: ts.Type, location: ts.Node) {
856+
if (!hasAllFlags(type.flags, ts.TypeFlags.Object)) {
868857
return false;
869858
}
870859

871-
return body.properties.every(
872-
(prop) =>
873-
ts.isPropertyAssignment(prop) &&
874-
ts.isStringLiteral(prop.initializer)
875-
);
860+
return type.getProperties().every((prop) => {
861+
const propType = checker.getTypeOfSymbolAtLocation(prop, location);
862+
return propType.isStringLiteral();
863+
});
876864
}
877865

878866
function convertVariableAsEnum(
@@ -889,22 +877,24 @@ function convertVariableAsEnum(
889877
const rc = context.withScope(reflection);
890878

891879
const declaration = symbol.declarations![0] as ts.VariableDeclaration;
892-
const init = (declaration.initializer as ts.AsExpression)
893-
.expression as ts.ObjectLiteralExpression;
880+
const type = context.checker.getTypeAtLocation(declaration);
894881

895-
for (const prop of init.properties as readonly ts.PropertyAssignment[]) {
896-
const childSymbol = context.checker.getSymbolAtLocation(prop.name);
882+
for (const prop of type.getProperties()) {
897883
const reflection = rc.createDeclarationReflection(
898884
ReflectionKind.EnumMember,
899-
childSymbol,
885+
prop,
900886
void 0
901887
);
902888

903-
reflection.defaultValue = JSON.stringify(
904-
(prop.initializer as ts.StringLiteral).text
889+
const propType = context.checker.getTypeOfSymbolAtLocation(
890+
prop,
891+
declaration
905892
);
893+
assert(propType.isStringLiteral());
894+
895+
reflection.defaultValue = JSON.stringify(propType.value);
906896

907-
rc.finalizeDeclarationReflection(reflection, childSymbol, void 0);
897+
rc.finalizeDeclarationReflection(reflection, prop, void 0);
908898
}
909899

910900
// Skip converting the type alias, if there is one

src/test/behaviorTests.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,28 @@ export const behaviorTests: Record<
1717
> = {
1818
asConstEnum(project) {
1919
const SomeEnumLike = query(project, "SomeEnumLike");
20-
equal(SomeEnumLike.kind, ReflectionKind.Variable);
20+
equal(SomeEnumLike.kind, ReflectionKind.Variable, "SomeEnumLike");
2121
const SomeEnumLikeTagged = query(project, "SomeEnumLikeTagged");
22-
equal(SomeEnumLikeTagged.kind, ReflectionKind.Enum);
22+
equal(
23+
SomeEnumLikeTagged.kind,
24+
ReflectionKind.Enum,
25+
"SomeEnumLikeTagged"
26+
);
2327
const A = query(project, "SomeEnumLikeTagged.a");
2428
equal(A.defaultValue, '"a"');
29+
30+
const ManualEnum = query(project, "ManualEnum");
31+
equal(ManualEnum.kind, ReflectionKind.Enum, "ManualEnum");
32+
33+
const ManualWithoutHelper = query(project, "ManualEnumHelper");
34+
equal(
35+
ManualWithoutHelper.kind,
36+
ReflectionKind.Enum,
37+
"ManualEnumHelper"
38+
);
39+
40+
const WithoutReadonly = query(project, "WithoutReadonly");
41+
equal(WithoutReadonly.kind, ReflectionKind.Enum, "WithoutReadonly");
2542
},
2643
duplicateHeritageClauses(project) {
2744
const b = query(project, "B");

src/test/converter2/behavior/asConstEnum.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,18 @@ export const SomeEnumLikeTagged = {
88
a: "a",
99
b: "b",
1010
} as const;
11+
12+
/** @enum */
13+
export const ManualEnum: { readonly a: "a" } = {
14+
a: "a",
15+
};
16+
17+
/** @enum */
18+
export const ManualEnumHelper: Readonly<{ a: "a" }> = {
19+
a: "a",
20+
};
21+
22+
/** @enum */
23+
export const WithoutReadonly = {
24+
a: "a",
25+
} as { a: "a" };

0 commit comments

Comments
 (0)