Investigate enum namespace declaration issue in declarations transformer

Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2025-06-25 15:56:10 +00:00
parent 67ff5e337a
commit e47e4b8f78
4 changed files with 95 additions and 21 deletions

View File

@ -50962,9 +50962,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return false;
}
function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression {
const enumResult = type.flags & TypeFlags.EnumLike ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, /*internalFlags*/ undefined, tracker)
: type === trueType ? factory.createTrue() : type === falseType && factory.createFalse();
function literalTypeToNode(type: FreshableType, enclosing: Node, tracker: SymbolTracker): Expression {
const enumResult = type.flags & TypeFlags.EnumLike ? nodeBuilder.symbolToExpression(type.symbol, SymbolFlags.Value, enclosing, /*flags*/ undefined, /*internalFlags*/ undefined, tracker)
: type === trueType ? factory.createTrue() : type === falseType && factory.createFalse();
if (enumResult) return enumResult;
const literalValue = (type as LiteralType).value;
return typeof literalValue === "object" ? factory.createBigIntLiteral(literalValue) :

View File

@ -103,7 +103,8 @@ import {
isExpandoPropertyDeclaration,
isExportAssignment,
isExportDeclaration,
isExpressionWithTypeArguments,
isExpressionWithTypeArguments,
isExpression,
isExternalModule,
isExternalModuleAugmentation,
isExternalModuleIndicator,
@ -128,8 +129,9 @@ import {
isObjectLiteralExpression,
isOmittedExpression,
isParameter,
isPrimitiveLiteralValue,
isPrivateIdentifier,
isPrimitiveLiteralValue,
isPrivateIdentifier,
isPropertyAccessExpression,
isSemicolonClassElement,
isSetAccessorDeclaration,
isSourceFile,
@ -200,8 +202,8 @@ import {
TransformationContext,
Transformer,
transformNodes,
tryCast,
TypeAliasDeclaration,
tryCast,
TypeAliasDeclaration,
TypeNode,
TypeParameterDeclaration,
TypeReferenceNode,
@ -654,21 +656,46 @@ export function transformDeclarations(context: TransformationContext): Transform
return newParam;
}
function shouldPrintWithInitializer(node: Node): node is CanHaveLiteralInitializer & { initializer: Expression; } {
return canHaveLiteralInitializer(node)
&& !!node.initializer
&& resolver.isLiteralConstDeclaration(getParseTreeNode(node) as CanHaveLiteralInitializer); // TODO: Make safea
function shouldPrintWithInitializer(node: Node): node is CanHaveLiteralInitializer & { initializer: Expression; } {
if (!canHaveLiteralInitializer(node) || !node.initializer || !resolver.isLiteralConstDeclaration(getParseTreeNode(node) as CanHaveLiteralInitializer)) {
return false;
}
// Check if the initializer is a property access to an enum member (e.g., Foo.bar)
// In this case, don't print with initializer - let it get a type annotation instead
const unwrappedInitializer = unwrapParenthesizedExpression(node.initializer);
if (isPropertyAccessExpression(unwrappedInitializer)) {
const constantValue = resolver.getConstantValue(unwrappedInitializer);
// If it has a constant value (meaning it's an enum member reference), use type instead of initializer
if (constantValue !== undefined) {
return false;
}
}
return true;
}
function ensureNoInitializer(node: CanHaveLiteralInitializer) {
if (shouldPrintWithInitializer(node)) {
const unwrappedInitializer = unwrapParenthesizedExpression(node.initializer);
if (!isPrimitiveLiteralValue(unwrappedInitializer)) {
reportInferenceFallback(node);
}
return resolver.createLiteralConstValue(getParseTreeNode(node, canHaveLiteralInitializer)!, symbolTracker);
}
return undefined;
function ensureNoInitializer(node: CanHaveLiteralInitializer) {
if (shouldPrintWithInitializer(node)) {
const unwrappedInitializer = unwrapParenthesizedExpression(node.initializer);
if (!isPrimitiveLiteralValue(unwrappedInitializer)) {
reportInferenceFallback(node);
}
// Check if the initializer is a property access to an enum member (e.g., Foo.bar)
// In this case, don't print with initializer - let it fall back to type annotation
if (isPropertyAccessExpression(unwrappedInitializer)) {
const constantValue = resolver.getConstantValue(unwrappedInitializer);
// If it has a constant value (meaning it's an enum member reference),
// don't use initializer and let it get a type instead
if (constantValue !== undefined) {
return undefined;
}
}
return resolver.createLiteralConstValue(getParseTreeNode(node, canHaveLiteralInitializer)!, symbolTracker);
}
return undefined;
}
function ensureType(node: VariableDeclaration | ParameterDeclaration | BindingElement | PropertyDeclaration | PropertySignature | ExportAssignment | SignatureDeclaration, ignorePrivate?: boolean): TypeNode | undefined {
if (!ignorePrivate && hasEffectiveModifier(node, ModifierFlags.Private)) {

View File

@ -0,0 +1,21 @@
declare enum Foo {
bar = 0
}
declare namespace Foo {
const baz = bar;
}
declare enum MyEnum {
First = 1,
Second = 2
}
declare namespace MyEnum {
const value1 = First;
const value2 = Second;
}
declare enum StringEnum {
Option1 = "option1",
Option2 = "option2"
}
declare namespace StringEnum {
const selected: any;
}

View File

@ -0,0 +1,26 @@
// Test for constant declarations inside namespace merged with enum
enum Foo {
bar
}
namespace Foo {
export const baz = Foo.bar;
}
// Multiple enum members
enum MyEnum {
First = 1,
Second = 2
}
namespace MyEnum {
export const value1 = MyEnum.First;
export const value2 = MyEnum.Second;
}
// String enum
enum StringEnum {
Option1 = "option1",
Option2 = "option2"
}
namespace StringEnum {
export const selected = StringEnum.Option1;
}