diff --git a/packages/compiler-cli/src/ngtsc/metadata/index.ts b/packages/compiler-cli/src/ngtsc/metadata/index.ts index 083955dad7..49b8226284 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/index.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/index.ts @@ -9,4 +9,4 @@ /// export {TypeScriptReflectionHost, filterToMembersWithDecorator, findMember, reflectObjectLiteral, reflectTypeEntityToDeclaration} from './src/reflector'; -export {AbsoluteReference, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver'; +export {AbsoluteReference, EnumValue, ImportMode, Reference, ResolvedReference, ResolvedValue, isDynamicValue, staticallyResolve} from './src/resolver'; diff --git a/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts b/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts index a0988a5e37..cb89986276 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/src/resolver.ts @@ -53,7 +53,7 @@ export function isDynamicValue(value: any): value is DynamicValue { * non-primitive value, or a special `DynamicValue` type which indicates the value was not * available statically. */ -export type ResolvedValue = number | boolean | string | null | undefined | Reference | +export type ResolvedValue = number | boolean | string | null | undefined | Reference | EnumValue | ResolvedValueArray | ResolvedValueMap | DynamicValue; /** @@ -72,6 +72,15 @@ export interface ResolvedValueArray extends Array {} * `ResolvedValue`. */ export interface ResolvedValueMap extends Map {} +/** + * A value member of an enumeration. + * + * Contains a `Reference` to the enumeration itself, and the name of the referenced member. + */ +export class EnumValue { + constructor(readonly enumRef: Reference, readonly name: string) {} +} + /** * Tracks the scope of a function body, which includes `ResolvedValue`s for the parameters of that * body. @@ -438,6 +447,8 @@ class StaticInterpreter { return context.scope.get(node) !; } else if (ts.isExportAssignment(node)) { return this.visitExpression(node.expression, context); + } else if (ts.isEnumDeclaration(node)) { + return this.visitEnumDeclaration(node, context); } else if (ts.isSourceFile(node)) { return this.visitSourceFile(node, context); } else { @@ -445,6 +456,18 @@ class StaticInterpreter { } } + private visitEnumDeclaration(node: ts.EnumDeclaration, context: Context): ResolvedValue { + const enumRef = this.getReference(node, context) as Reference; + const map = new Map(); + node.members.forEach(member => { + const name = this.stringNameFromPropertyName(member.name, context); + if (name !== undefined) { + map.set(name, new EnumValue(enumRef, name)); + } + }); + return map; + } + private visitElementAccessExpression(node: ts.ElementAccessExpression, context: Context): ResolvedValue { const lhs = this.visitExpression(node.expression, context); @@ -689,6 +712,8 @@ function literal(value: ResolvedValue): any { function identifierOfDeclaration(decl: ts.Declaration): ts.Identifier|undefined { if (ts.isClassDeclaration(decl)) { return decl.name; + } else if (ts.isEnumDeclaration(decl)) { + return decl.name; } else if (ts.isFunctionDeclaration(decl)) { return decl.name; } else if (ts.isVariableDeclaration(decl) && ts.isIdentifier(decl.name)) { diff --git a/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts b/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts index ee34ca4501..7c4863a6dd 100644 --- a/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts +++ b/packages/compiler-cli/src/ngtsc/metadata/test/resolver_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {TypeScriptReflectionHost} from '..'; import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript'; -import {AbsoluteReference, Reference, ResolvedValue, staticallyResolve} from '../src/resolver'; +import {AbsoluteReference, EnumValue, Reference, ResolvedValue, staticallyResolve} from '../src/resolver'; function makeSimpleProgram(contents: string): ts.Program { return makeProgram([{name: 'entry.ts', contents}]).program; @@ -265,4 +265,23 @@ describe('ngtsc metadata', () => { it('template expressions work', () => { expect(evaluate('const a = 2, b = 4;', '`1${a}3${b}5`')).toEqual('12345'); }); + + it('enum resolution works', () => { + const result = evaluate( + ` + enum Foo { + A, + B, + C, + } + + const r = Foo.B; + `, + 'r'); + if (!(result instanceof EnumValue)) { + return fail(`result is not an EnumValue`); + } + expect(result.enumRef.node.name.text).toBe('Foo'); + expect(result.name).toBe('B'); + }); });