feat(ivy): support enum values in static resolution (#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 #25619
This commit is contained in:
parent
61218f5f0b
commit
5c95b4b3a3
|
@ -9,4 +9,4 @@
|
|||
/// <reference types="node" />
|
||||
|
||||
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';
|
||||
|
|
|
@ -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> {}
|
|||
* `ResolvedValue`.
|
||||
*/ export interface ResolvedValueMap extends Map<string, ResolvedValue> {}
|
||||
|
||||
/**
|
||||
* 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<ts.EnumDeclaration>, 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<ts.EnumDeclaration>;
|
||||
const map = new Map<string, EnumValue>();
|
||||
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)) {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue