@@ -47,6 +47,7 @@ namespace ts {
47
47
48
48
let typeCount = 0;
49
49
let symbolCount = 0;
50
+ let enumCount = 0;
50
51
let symbolInstantiationDepth = 0;
51
52
52
53
const emptyArray: any[] = [];
@@ -210,8 +211,7 @@ namespace ts {
210
211
const tupleTypes: GenericType[] = [];
211
212
const unionTypes = createMap<UnionType>();
212
213
const intersectionTypes = createMap<IntersectionType>();
213
- const stringLiteralTypes = createMap<LiteralType>();
214
- const numericLiteralTypes = createMap<LiteralType>();
214
+ const literalTypes = createMap<LiteralType>();
215
215
const indexedAccessTypes = createMap<IndexedAccessType>();
216
216
const evolvingArrayTypes: EvolvingArrayType[] = [];
217
217
@@ -4904,34 +4904,36 @@ namespace ts {
4904
4904
return links.declaredType;
4905
4905
}
4906
4906
4907
- function isLiteralEnumMember(symbol: Symbol, member: EnumMember) {
4907
+ function isLiteralEnumMember(member: EnumMember) {
4908
4908
const expr = member.initializer;
4909
4909
if (!expr) {
4910
4910
return !isInAmbientContext(member);
4911
4911
}
4912
- return expr.kind === SyntaxKind.NumericLiteral ||
4912
+ return expr.kind === SyntaxKind.StringLiteral || expr.kind === SyntaxKind. NumericLiteral ||
4913
4913
expr.kind === SyntaxKind.PrefixUnaryExpression && (<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusToken &&
4914
4914
(<PrefixUnaryExpression>expr).operand.kind === SyntaxKind.NumericLiteral ||
4915
- expr.kind === SyntaxKind.Identifier && !!symbol. exports.get((<Identifier>expr).text);
4915
+ expr.kind === SyntaxKind.Identifier && (nodeIsMissing(expr) || !!getSymbolOfNode(member.parent). exports.get((<Identifier>expr).text) );
4916
4916
}
4917
4917
4918
- function enumHasLiteralMembers(symbol: Symbol) {
4918
+ function getEnumKind(symbol: Symbol): EnumKind {
4919
+ const links = getSymbolLinks(symbol);
4920
+ if (links.enumKind !== undefined) {
4921
+ return links.enumKind;
4922
+ }
4923
+ let hasNonLiteralMember = false;
4919
4924
for (const declaration of symbol.declarations) {
4920
4925
if (declaration.kind === SyntaxKind.EnumDeclaration) {
4921
4926
for (const member of (<EnumDeclaration>declaration).members) {
4922
- if (!isLiteralEnumMember(symbol, member)) {
4923
- return false;
4927
+ if (member.initializer && member.initializer.kind === SyntaxKind.StringLiteral) {
4928
+ return links.enumKind = EnumKind.Literal;
4929
+ }
4930
+ if (!isLiteralEnumMember(member)) {
4931
+ hasNonLiteralMember = true;
4924
4932
}
4925
4933
}
4926
4934
}
4927
4935
}
4928
- return true;
4929
- }
4930
-
4931
- function createEnumLiteralType(symbol: Symbol, value: number) {
4932
- const type = createLiteralType(TypeFlags.NumberLiteral | TypeFlags.EnumLiteral, value);
4933
- type.symbol = symbol;
4934
- return type;
4936
+ return links.enumKind = hasNonLiteralMember ? EnumKind.Numeric : EnumKind.Literal;
4935
4937
}
4936
4938
4937
4939
function getBaseTypeOfEnumLiteralType(type: Type) {
@@ -4943,17 +4945,14 @@ namespace ts {
4943
4945
if (links.declaredType) {
4944
4946
return links.declaredType;
4945
4947
}
4946
- if (enumHasLiteralMembers(symbol)) {
4948
+ if (getEnumKind(symbol) === EnumKind.Literal) {
4949
+ enumCount++;
4947
4950
const memberTypeList: Type[] = [];
4948
- const memberTypes: LiteralType[] = [];
4949
4951
for (const declaration of symbol.declarations) {
4950
4952
if (declaration.kind === SyntaxKind.EnumDeclaration) {
4951
- computeEnumMemberValues(<EnumDeclaration>declaration);
4952
4953
for (const member of (<EnumDeclaration>declaration).members) {
4953
- const memberSymbol = getSymbolOfNode(member);
4954
- const value = getEnumMemberValue(member);
4955
- if (!memberTypes[value]) {
4956
- const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, value);
4954
+ const memberType = getLiteralType(getEnumMemberValue(member), enumCount, getSymbolOfNode(member));
4955
+ if (!contains(memberTypeList, memberType)) {
4957
4956
memberTypeList.push(memberType);
4958
4957
}
4959
4958
}
@@ -4963,7 +4962,7 @@ namespace ts {
4963
4962
for (const declaration of symbol.declarations) {
4964
4963
if (declaration.kind === SyntaxKind.EnumDeclaration) {
4965
4964
for (const member of (<EnumDeclaration>declaration).members) {
4966
- getSymbolLinks(getSymbolOfNode(member)).declaredType = memberTypes[ getEnumMemberValue(member)] ;
4965
+ getSymbolLinks(getSymbolOfNode(member)).declaredType = getLiteralType( getEnumMemberValue(member), enumCount, getSymbolOfNode(member)) ;
4967
4966
}
4968
4967
}
4969
4968
}
@@ -7517,16 +7516,17 @@ namespace ts {
7517
7516
return prop.flags & SymbolFlags.Method && find(prop.declarations, decl => isClassLike(decl.parent));
7518
7517
}
7519
7518
7520
- function createLiteralType(flags: TypeFlags, value: string | number) {
7519
+ function createLiteralType(flags: TypeFlags, value: string | number, symbol: Symbol ) {
7521
7520
const type = <LiteralType>createType(flags);
7521
+ type.symbol = symbol;
7522
7522
type.value = value;
7523
7523
return type;
7524
7524
}
7525
7525
7526
7526
function getFreshTypeOfLiteralType(type: Type) {
7527
7527
if (type.flags & TypeFlags.StringOrNumberLiteral && !(type.flags & TypeFlags.FreshLiteral)) {
7528
7528
if (!(<LiteralType>type).freshType) {
7529
- const freshType = <LiteralType>createLiteralType(type.flags | TypeFlags.FreshLiteral, (<LiteralType>type).value);
7529
+ const freshType = <LiteralType>createLiteralType(type.flags | TypeFlags.FreshLiteral, (<LiteralType>type).value, (<LiteralType>type).symbol );
7530
7530
freshType.regularType = <LiteralType>type;
7531
7531
(<LiteralType>type).freshType = freshType;
7532
7532
}
@@ -7539,12 +7539,17 @@ namespace ts {
7539
7539
return type.flags & TypeFlags.StringOrNumberLiteral && type.flags & TypeFlags.FreshLiteral ? (<LiteralType>type).regularType : type;
7540
7540
}
7541
7541
7542
- function getLiteralType(value: string | number) {
7543
- const map = typeof value === "number" ? numericLiteralTypes : stringLiteralTypes;
7544
- const text = "" + value;
7545
- let type = map.get(text);
7542
+ function getLiteralType(value: string | number, enumId?: number, symbol?: Symbol) {
7543
+ // We store all literal types in a single map with keys of the form '#NNN' and '@SSS',
7544
+ // where NNN is the text representation of a numeric literal and SSS are the characters
7545
+ // of a string literal. For literal enum members we use 'EEE#NNN' and 'EEE@SSS', where
7546
+ // EEE is a unique id for the containing enum type.
7547
+ const qualifier = typeof value === "number" ? "#" : "@";
7548
+ const key = enumId ? enumId + qualifier + value : qualifier + value;
7549
+ let type = literalTypes.get(key);
7546
7550
if (!type) {
7547
- map.set(text, type = createLiteralType(typeof value === "number" ? TypeFlags.NumberLiteral : TypeFlags.StringLiteral, value));
7551
+ const flags = (typeof value === "number" ? TypeFlags.NumberLiteral : TypeFlags.StringLiteral) | (enumId ? TypeFlags.EnumLiteral : 0);
7552
+ literalTypes.set(key, type = createLiteralType(flags, value, symbol));
7548
7553
}
7549
7554
return type;
7550
7555
}
@@ -20716,17 +20721,22 @@ namespace ts {
20716
20721
return undefined;
20717
20722
}
20718
20723
20719
- function computeConstantValue(member: EnumMember): number {
20724
+ function computeConstantValue(member: EnumMember): string | number {
20725
+ const enumKind = getEnumKind(getSymbolOfNode(member.parent));
20720
20726
const isConstEnum = isConst(member.parent);
20721
20727
const initializer = member.initializer;
20722
- const value = evaluate (member. initializer);
20728
+ const value = enumKind === EnumKind.Literal && !isLiteralEnumMember (member) ? undefined : evaluate( initializer);
20723
20729
if (value !== undefined) {
20724
- if (isConstEnum && !isFinite(value)) {
20730
+ if (isConstEnum && typeof value === "number" && !isFinite(value)) {
20725
20731
error(initializer, isNaN(value) ?
20726
20732
Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN :
20727
20733
Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
20728
20734
}
20729
20735
}
20736
+ else if (enumKind === EnumKind.Literal) {
20737
+ error(initializer, Diagnostics.Computed_values_are_not_permitted_in_an_enum_with_string_valued_members);
20738
+ return 0;
20739
+ }
20730
20740
else if (isConstEnum) {
20731
20741
error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
20732
20742
}
@@ -20739,7 +20749,7 @@ namespace ts {
20739
20749
}
20740
20750
return value;
20741
20751
20742
- function evaluate(expr: Expression): number {
20752
+ function evaluate(expr: Expression): string | number {
20743
20753
switch (expr.kind) {
20744
20754
case SyntaxKind.PrefixUnaryExpression:
20745
20755
const value = evaluate((<PrefixUnaryExpression>expr).operand);
@@ -20770,13 +20780,15 @@ namespace ts {
20770
20780
}
20771
20781
}
20772
20782
break;
20783
+ case SyntaxKind.StringLiteral:
20784
+ return (<StringLiteral>expr).text;
20773
20785
case SyntaxKind.NumericLiteral:
20774
20786
checkGrammarNumericLiteral(<NumericLiteral>expr);
20775
20787
return +(<NumericLiteral>expr).text;
20776
20788
case SyntaxKind.ParenthesizedExpression:
20777
20789
return evaluate((<ParenthesizedExpression>expr).expression);
20778
20790
case SyntaxKind.Identifier:
20779
- return evaluateEnumMember(expr, getSymbolOfNode(member.parent), (<Identifier>expr).text);
20791
+ return nodeIsMissing(expr) ? 0 : evaluateEnumMember(expr, getSymbolOfNode(member.parent), (<Identifier>expr).text);
20780
20792
case SyntaxKind.ElementAccessExpression:
20781
20793
case SyntaxKind.PropertyAccessExpression:
20782
20794
if (isConstantMemberAccess(expr)) {
@@ -22476,7 +22488,7 @@ namespace ts {
22476
22488
return getNodeLinks(node).flags;
22477
22489
}
22478
22490
22479
- function getEnumMemberValue(node: EnumMember): number {
22491
+ function getEnumMemberValue(node: EnumMember): string | number {
22480
22492
computeEnumMemberValues(<EnumDeclaration>node.parent);
22481
22493
return getNodeLinks(node).enumMemberValue;
22482
22494
}
@@ -22491,7 +22503,7 @@ namespace ts {
22491
22503
return false;
22492
22504
}
22493
22505
22494
- function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number {
22506
+ function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number {
22495
22507
if (node.kind === SyntaxKind.EnumMember) {
22496
22508
return getEnumMemberValue(<EnumMember>node);
22497
22509
}
0 commit comments