Skip to content

Commit 11224e2

Browse files
alxhubFrederikSchlemmer
authored andcommitted
feat(ivy): support enum values in static resolution (angular#25619)
This commit adds support for enumeration values. An enumeration value is now a first-class return value of the resolver, which provides both a Reference to the enum type itself and the name of the value from that enum. Resolving an enum itself returns a Map<string, EnumValue>. PR Close angular#25619
1 parent be2d52b commit 11224e2

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

packages/compiler-cli/src/ngtsc/metadata/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
/// <reference types="node" />
1010

1111
export {TypeScriptReflectionHost, filterToMembersWithDecorator, findMember, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/reflector';
12-
export {AbsoluteReference, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver';
12+
export {AbsoluteReference, EnumValue, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver';

packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts

+26-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function isDynamicValue(value: any): value is DynamicValue {
5353
* non-primitive value, or a special `DynamicValue` type which indicates the value was not
5454
* available statically.
5555
*/
56-
export type ResolvedValue = number | boolean | string | null | undefined | Reference |
56+
export type ResolvedValue = number | boolean | string | null | undefined | Reference | EnumValue |
5757
ResolvedValueArray | ResolvedValueMap | DynamicValue;
5858

5959
/**
@@ -72,6 +72,15 @@ export interface ResolvedValueArray extends Array<ResolvedValue> {}
7272
* `ResolvedValue`.
7373
*/ export interface ResolvedValueMap extends Map<string, ResolvedValue> {}
7474

75+
/**
76+
* A value member of an enumeration.
77+
*
78+
* Contains a `Reference` to the enumeration itself, and the name of the referenced member.
79+
*/
80+
export class EnumValue {
81+
constructor(readonly enumRef: Reference<ts.EnumDeclaration>, readonly name: string) {}
82+
}
83+
7584
/**
7685
* Tracks the scope of a function body, which includes `ResolvedValue`s for the parameters of that
7786
* body.
@@ -438,13 +447,27 @@ class StaticInterpreter {
438447
return context.scope.get(node) !;
439448
} else if (ts.isExportAssignment(node)) {
440449
return this.visitExpression(node.expression, context);
450+
} else if (ts.isEnumDeclaration(node)) {
451+
return this.visitEnumDeclaration(node, context);
441452
} else if (ts.isSourceFile(node)) {
442453
return this.visitSourceFile(node, context);
443454
} else {
444455
return this.getReference(node, context);
445456
}
446457
}
447458

459+
private visitEnumDeclaration(node: ts.EnumDeclaration, context: Context): ResolvedValue {
460+
const enumRef = this.getReference(node, context) as Reference<ts.EnumDeclaration>;
461+
const map = new Map<string, EnumValue>();
462+
node.members.forEach(member => {
463+
const name = this.stringNameFromPropertyName(member.name, context);
464+
if (name !== undefined) {
465+
map.set(name, new EnumValue(enumRef, name));
466+
}
467+
});
468+
return map;
469+
}
470+
448471
private visitElementAccessExpression(node: ts.ElementAccessExpression, context: Context):
449472
ResolvedValue {
450473
const lhs = this.visitExpression(node.expression, context);
@@ -689,6 +712,8 @@ function literal(value: ResolvedValue): any {
689712
function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined {
690713
if (ts.isClassDeclaration(decl)) {
691714
return decl.name;
715+
} else if (ts.isEnumDeclaration(decl)) {
716+
return decl.name;
692717
} else if (ts.isFunctionDeclaration(decl)) {
693718
return decl.name;
694719
} else if (ts.isVariableDeclaration(decl) && ts.isIdentifier(decl.name)) {

packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import * as ts from 'typescript';
1111

1212
import {TypeScriptReflectionHost} from '..';
1313
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
14-
import {AbsoluteReference, Reference, ResolvedValue, staticallyResolve} from '../src/resolver';
14+
import {AbsoluteReference, EnumValue, Reference, ResolvedValue, staticallyResolve} from '../src/resolver';
1515

1616
function makeSimpleProgram(contents: string): ts.Program {
1717
return makeProgram([{name: 'entry.ts', contents}]).program;
@@ -265,4 +265,23 @@ describe('ngtsc metadata', () => {
265265

266266
it('template expressions work',
267267
() => { expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345'); });
268+
269+
it('enum resolution works', () => {
270+
const result = evaluate(
271+
`
272+
enum Foo {
273+
A,
274+
B,
275+
C,
276+
}
277+
278+
const r = Foo.B;
279+
`,
280+
'r');
281+
if (!(result instanceof EnumValue)) {
282+
return fail(`result is not an EnumValue`);
283+
}
284+
expect(result.enumRef.node.name.text).toBe('Foo');
285+
expect(result.name).toBe('B');
286+
});
268287
});

0 commit comments

Comments
 (0)