@@ -167,6 +167,8 @@ import {
167
167
equateValues,
168
168
escapeLeadingUnderscores,
169
169
escapeString,
170
+ EvaluatorResult,
171
+ evaluatorResult,
170
172
every,
171
173
EvolvingArrayType,
172
174
ExclamationToken,
@@ -723,7 +725,6 @@ import {
723
725
isStringOrNumericLiteralLike,
724
726
isSuperCall,
725
727
isSuperProperty,
726
- isSyntacticallyString,
727
728
isTaggedTemplateExpression,
728
729
isTemplateSpan,
729
730
isThisContainerOrFunctionBlock,
@@ -12896,7 +12897,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
12896
12897
for (const member of (declaration as EnumDeclaration).members) {
12897
12898
if (hasBindableName(member)) {
12898
12899
const memberSymbol = getSymbolOfDeclaration(member);
12899
- const value = getEnumMemberValue(member);
12900
+ const value = getEnumMemberValue(member).value ;
12900
12901
const memberType = getFreshTypeOfLiteralType(
12901
12902
value !== undefined ?
12902
12903
getEnumLiteralType(value, getSymbolId(symbol), memberSymbol) :
@@ -21335,8 +21336,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
21335
21336
}
21336
21337
return false;
21337
21338
}
21338
- const sourceValue = getEnumMemberValue(getDeclarationOfKind(sourceProperty, SyntaxKind.EnumMember)!);
21339
- const targetValue = getEnumMemberValue(getDeclarationOfKind(targetProperty, SyntaxKind.EnumMember)!);
21339
+ const sourceValue = getEnumMemberValue(getDeclarationOfKind(sourceProperty, SyntaxKind.EnumMember)!).value ;
21340
+ const targetValue = getEnumMemberValue(getDeclarationOfKind(targetProperty, SyntaxKind.EnumMember)!).value ;
21340
21341
if (sourceValue !== targetValue) {
21341
21342
const sourceIsString = typeof sourceValue === "string";
21342
21343
const targetIsString = typeof targetValue === "string";
@@ -39494,7 +39495,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
39494
39495
if (isConstContext(node) || isTemplateLiteralContext(node) || someType(getContextualType(node, /*contextFlags*/ undefined) || unknownType, isTemplateLiteralContextualType)) {
39495
39496
return getTemplateLiteralType(texts, types);
39496
39497
}
39497
- const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluate(node);
39498
+ const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluate(node).value ;
39498
39499
return evaluated ? getFreshTypeOfLiteralType(getStringLiteralType(evaluated)) : stringType;
39499
39500
}
39500
39501
@@ -45903,15 +45904,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45903
45904
let autoValue: number | undefined = 0;
45904
45905
let previous: EnumMember | undefined;
45905
45906
for (const member of node.members) {
45906
- const value = computeMemberValue (member, autoValue, previous);
45907
- getNodeLinks(member).enumMemberValue = value ;
45908
- autoValue = typeof value === "number" ? value + 1 : undefined;
45907
+ const result = computeEnumMemberValue (member, autoValue, previous);
45908
+ getNodeLinks(member).enumMemberValue = result ;
45909
+ autoValue = typeof result. value === "number" ? result. value + 1 : undefined;
45909
45910
previous = member;
45910
45911
}
45911
45912
}
45912
45913
}
45913
45914
45914
- function computeMemberValue (member: EnumMember, autoValue: number | undefined, previous: EnumMember | undefined) {
45915
+ function computeEnumMemberValue (member: EnumMember, autoValue: number | undefined, previous: EnumMember | undefined): EvaluatorResult {
45915
45916
if (isComputedNonLiteralName(member.name)) {
45916
45917
error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
45917
45918
}
@@ -45922,44 +45923,47 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45922
45923
}
45923
45924
}
45924
45925
if (member.initializer) {
45925
- return computeConstantValue (member);
45926
+ return computeConstantEnumMemberValue (member);
45926
45927
}
45927
45928
// In ambient non-const numeric enum declarations, enum members without initializers are
45928
45929
// considered computed members (as opposed to having auto-incremented values).
45929
45930
if (member.parent.flags & NodeFlags.Ambient && !isEnumConst(member.parent)) {
45930
- return undefined;
45931
+ return evaluatorResult(/*value*/ undefined) ;
45931
45932
}
45932
45933
// If the member declaration specifies no value, the member is considered a constant enum member.
45933
45934
// If the member is the first member in the enum declaration, it is assigned the value zero.
45934
45935
// Otherwise, it is assigned the value of the immediately preceding member plus one, and an error
45935
45936
// occurs if the immediately preceding member is not a constant enum member.
45936
45937
if (autoValue === undefined) {
45937
45938
error(member.name, Diagnostics.Enum_member_must_have_initializer);
45938
- return undefined;
45939
+ return evaluatorResult(/*value*/ undefined) ;
45939
45940
}
45940
- if (getIsolatedModules(compilerOptions) && previous?.initializer && !isSyntacticallyNumericConstant(previous.initializer)) {
45941
- error(
45942
- member.name,
45943
- Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled,
45944
- );
45941
+ if (getIsolatedModules(compilerOptions) && previous?.initializer) {
45942
+ const prevValue = getEnumMemberValue(previous);
45943
+ if (!(typeof prevValue.value === "number" && !prevValue.resolvedOtherFiles)) {
45944
+ error(
45945
+ member.name,
45946
+ Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled,
45947
+ );
45948
+ }
45945
45949
}
45946
- return autoValue;
45950
+ return evaluatorResult( autoValue) ;
45947
45951
}
45948
45952
45949
- function computeConstantValue (member: EnumMember): string | number | undefined {
45953
+ function computeConstantEnumMemberValue (member: EnumMember): EvaluatorResult {
45950
45954
const isConstEnum = isEnumConst(member.parent);
45951
45955
const initializer = member.initializer!;
45952
- const value = evaluate(initializer, member);
45953
- if (value !== undefined) {
45954
- if (isConstEnum && typeof value === "number" && !isFinite(value)) {
45956
+ const result = evaluate(initializer, member);
45957
+ if (result. value !== undefined) {
45958
+ if (isConstEnum && typeof result. value === "number" && !isFinite(result. value)) {
45955
45959
error(
45956
45960
initializer,
45957
- isNaN(value) ?
45961
+ isNaN(result. value) ?
45958
45962
Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN :
45959
45963
Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value,
45960
45964
);
45961
45965
}
45962
- else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer) ) {
45966
+ else if (getIsolatedModules(compilerOptions) && typeof result. value === "string" && !result. isSyntacticallyString) {
45963
45967
error(
45964
45968
initializer,
45965
45969
Diagnostics._0_has_a_string_type_but_must_have_syntactically_recognizable_string_syntax_when_isolatedModules_is_enabled,
@@ -45976,30 +45980,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
45976
45980
else {
45977
45981
checkTypeAssignableTo(checkExpression(initializer), numberType, initializer, Diagnostics.Type_0_is_not_assignable_to_type_1_as_required_for_computed_enum_member_values);
45978
45982
}
45979
- return value;
45980
- }
45981
-
45982
- function isSyntacticallyNumericConstant(expr: Expression): boolean {
45983
- expr = skipOuterExpressions(expr);
45984
- switch (expr.kind) {
45985
- case SyntaxKind.PrefixUnaryExpression:
45986
- return isSyntacticallyNumericConstant((expr as PrefixUnaryExpression).operand);
45987
- case SyntaxKind.BinaryExpression:
45988
- return isSyntacticallyNumericConstant((expr as BinaryExpression).left) && isSyntacticallyNumericConstant((expr as BinaryExpression).right);
45989
- case SyntaxKind.NumericLiteral:
45990
- return true;
45991
- }
45992
- return false;
45983
+ return result;
45993
45984
}
45994
45985
45995
45986
function evaluateEntityNameExpression(expr: EntityNameExpression, location?: Declaration) {
45996
45987
const symbol = resolveEntityName(expr, SymbolFlags.Value, /*ignoreErrors*/ true);
45997
- if (!symbol) return undefined;
45988
+ if (!symbol) return evaluatorResult(/*value*/ undefined) ;
45998
45989
45999
45990
if (expr.kind === SyntaxKind.Identifier) {
46000
45991
const identifier = expr;
46001
45992
if (isInfinityOrNaNString(identifier.escapedText) && (symbol === getGlobalSymbol(identifier.escapedText, SymbolFlags.Value, /*diagnostic*/ undefined))) {
46002
- return +(identifier.escapedText);
45993
+ // Technically we resolved a global lib file here, but the decision to treat this as numeric
45994
+ // is more predicated on the fact that the single-file resolution *didn't* resolve to a
45995
+ // different meaning of `Infinity` or `NaN`. Transpilers handle this no problem.
45996
+ return evaluatorResult(+(identifier.escapedText), /*isSyntacticallyString*/ false);
46003
45997
}
46004
45998
}
46005
45999
@@ -46009,9 +46003,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46009
46003
if (isConstantVariable(symbol)) {
46010
46004
const declaration = symbol.valueDeclaration;
46011
46005
if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) {
46012
- return evaluate(declaration.initializer, declaration);
46006
+ const result = evaluate(declaration.initializer, declaration);
46007
+ if (location && getSourceFileOfNode(location) !== getSourceFileOfNode(declaration)) {
46008
+ return evaluatorResult(
46009
+ result.value,
46010
+ /*isSyntacticallyString*/ false,
46011
+ /*resolvedOtherFiles*/ true,
46012
+ );
46013
+ }
46014
+ return result;
46013
46015
}
46014
46016
}
46017
+ return evaluatorResult(/*value*/ undefined);
46015
46018
}
46016
46019
46017
46020
function evaluateElementAccessExpression(expr: ElementAccessExpression, location?: Declaration) {
@@ -46022,21 +46025,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46022
46025
const name = escapeLeadingUnderscores(expr.argumentExpression.text);
46023
46026
const member = rootSymbol.exports!.get(name);
46024
46027
if (member) {
46028
+ Debug.assert(getSourceFileOfNode(member.valueDeclaration) === getSourceFileOfNode(rootSymbol.valueDeclaration));
46025
46029
return location ? evaluateEnumMember(expr, member, location) : getEnumMemberValue(member.valueDeclaration as EnumMember);
46026
46030
}
46027
46031
}
46028
46032
}
46033
+ return evaluatorResult(/*value*/ undefined);
46029
46034
}
46030
46035
46031
46036
function evaluateEnumMember(expr: Expression, symbol: Symbol, location: Declaration) {
46032
46037
const declaration = symbol.valueDeclaration;
46033
46038
if (!declaration || declaration === location) {
46034
46039
error(expr, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(symbol));
46035
- return undefined;
46040
+ return evaluatorResult(/*value*/ undefined) ;
46036
46041
}
46037
46042
if (!isBlockScopedNameDeclaredBeforeUse(declaration, location)) {
46038
46043
error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
46039
- return 0 ;
46044
+ return evaluatorResult(/*value*/ 0) ;
46040
46045
}
46041
46046
return getEnumMemberValue(declaration as EnumMember);
46042
46047
}
@@ -48668,9 +48673,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
48668
48673
return nodeLinks[nodeId]?.flags || 0;
48669
48674
}
48670
48675
48671
- function getEnumMemberValue(node: EnumMember): string | number | undefined {
48676
+ function getEnumMemberValue(node: EnumMember): EvaluatorResult {
48672
48677
computeEnumMemberValues(node.parent);
48673
- return getNodeLinks(node).enumMemberValue;
48678
+ return getNodeLinks(node).enumMemberValue ?? evaluatorResult(/*value*/ undefined) ;
48674
48679
}
48675
48680
48676
48681
function canHaveConstantValue(node: Node): node is EnumMember | AccessExpression {
@@ -48685,15 +48690,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
48685
48690
48686
48691
function getConstantValue(node: EnumMember | AccessExpression): string | number | undefined {
48687
48692
if (node.kind === SyntaxKind.EnumMember) {
48688
- return getEnumMemberValue(node);
48693
+ return getEnumMemberValue(node).value ;
48689
48694
}
48690
48695
48691
48696
const symbol = getNodeLinks(node).resolvedSymbol;
48692
48697
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
48693
48698
// inline property\index accesses only for const enums
48694
48699
const member = symbol.valueDeclaration as EnumMember;
48695
48700
if (isEnumConst(member.parent)) {
48696
- return getEnumMemberValue(member);
48701
+ return getEnumMemberValue(member).value ;
48697
48702
}
48698
48703
}
48699
48704
@@ -49096,6 +49101,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
49096
49101
const node = getParseTreeNode(nodeIn, canHaveConstantValue);
49097
49102
return node ? getConstantValue(node) : undefined;
49098
49103
},
49104
+ getEnumMemberValue: nodeIn => {
49105
+ const node = getParseTreeNode(nodeIn, isEnumMember);
49106
+ return node ? getEnumMemberValue(node) : undefined;
49107
+ },
49099
49108
collectLinkedAliases,
49100
49109
getReferencedValueDeclaration,
49101
49110
getReferencedValueDeclarations,
0 commit comments