mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:32:08 -06:00
Use evaluator for isolatedModules enum restrictions (#57966)
This commit is contained in:
parent
83e3d6ae59
commit
9f8a231270
@ -167,6 +167,8 @@ import {
|
||||
equateValues,
|
||||
escapeLeadingUnderscores,
|
||||
escapeString,
|
||||
EvaluatorResult,
|
||||
evaluatorResult,
|
||||
every,
|
||||
EvolvingArrayType,
|
||||
ExclamationToken,
|
||||
@ -723,7 +725,6 @@ import {
|
||||
isStringOrNumericLiteralLike,
|
||||
isSuperCall,
|
||||
isSuperProperty,
|
||||
isSyntacticallyString,
|
||||
isTaggedTemplateExpression,
|
||||
isTemplateSpan,
|
||||
isThisContainerOrFunctionBlock,
|
||||
@ -12896,7 +12897,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
for (const member of (declaration as EnumDeclaration).members) {
|
||||
if (hasBindableName(member)) {
|
||||
const memberSymbol = getSymbolOfDeclaration(member);
|
||||
const value = getEnumMemberValue(member);
|
||||
const value = getEnumMemberValue(member).value;
|
||||
const memberType = getFreshTypeOfLiteralType(
|
||||
value !== undefined ?
|
||||
getEnumLiteralType(value, getSymbolId(symbol), memberSymbol) :
|
||||
@ -21335,8 +21336,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const sourceValue = getEnumMemberValue(getDeclarationOfKind(sourceProperty, SyntaxKind.EnumMember)!);
|
||||
const targetValue = getEnumMemberValue(getDeclarationOfKind(targetProperty, SyntaxKind.EnumMember)!);
|
||||
const sourceValue = getEnumMemberValue(getDeclarationOfKind(sourceProperty, SyntaxKind.EnumMember)!).value;
|
||||
const targetValue = getEnumMemberValue(getDeclarationOfKind(targetProperty, SyntaxKind.EnumMember)!).value;
|
||||
if (sourceValue !== targetValue) {
|
||||
const sourceIsString = typeof sourceValue === "string";
|
||||
const targetIsString = typeof targetValue === "string";
|
||||
@ -39494,7 +39495,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (isConstContext(node) || isTemplateLiteralContext(node) || someType(getContextualType(node, /*contextFlags*/ undefined) || unknownType, isTemplateLiteralContextualType)) {
|
||||
return getTemplateLiteralType(texts, types);
|
||||
}
|
||||
const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluate(node);
|
||||
const evaluated = node.parent.kind !== SyntaxKind.TaggedTemplateExpression && evaluate(node).value;
|
||||
return evaluated ? getFreshTypeOfLiteralType(getStringLiteralType(evaluated)) : stringType;
|
||||
}
|
||||
|
||||
@ -45903,15 +45904,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let autoValue: number | undefined = 0;
|
||||
let previous: EnumMember | undefined;
|
||||
for (const member of node.members) {
|
||||
const value = computeMemberValue(member, autoValue, previous);
|
||||
getNodeLinks(member).enumMemberValue = value;
|
||||
autoValue = typeof value === "number" ? value + 1 : undefined;
|
||||
const result = computeEnumMemberValue(member, autoValue, previous);
|
||||
getNodeLinks(member).enumMemberValue = result;
|
||||
autoValue = typeof result.value === "number" ? result.value + 1 : undefined;
|
||||
previous = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function computeMemberValue(member: EnumMember, autoValue: number | undefined, previous: EnumMember | undefined) {
|
||||
function computeEnumMemberValue(member: EnumMember, autoValue: number | undefined, previous: EnumMember | undefined): EvaluatorResult {
|
||||
if (isComputedNonLiteralName(member.name)) {
|
||||
error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
|
||||
}
|
||||
@ -45922,12 +45923,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
if (member.initializer) {
|
||||
return computeConstantValue(member);
|
||||
return computeConstantEnumMemberValue(member);
|
||||
}
|
||||
// In ambient non-const numeric enum declarations, enum members without initializers are
|
||||
// considered computed members (as opposed to having auto-incremented values).
|
||||
if (member.parent.flags & NodeFlags.Ambient && !isEnumConst(member.parent)) {
|
||||
return undefined;
|
||||
return evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
// If the member declaration specifies no value, the member is considered a constant enum member.
|
||||
// If the member is the first member in the enum declaration, it is assigned the value zero.
|
||||
@ -45935,31 +45936,34 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// occurs if the immediately preceding member is not a constant enum member.
|
||||
if (autoValue === undefined) {
|
||||
error(member.name, Diagnostics.Enum_member_must_have_initializer);
|
||||
return undefined;
|
||||
return evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
if (getIsolatedModules(compilerOptions) && previous?.initializer && !isSyntacticallyNumericConstant(previous.initializer)) {
|
||||
error(
|
||||
member.name,
|
||||
Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled,
|
||||
);
|
||||
if (getIsolatedModules(compilerOptions) && previous?.initializer) {
|
||||
const prevValue = getEnumMemberValue(previous);
|
||||
if (!(typeof prevValue.value === "number" && !prevValue.resolvedOtherFiles)) {
|
||||
error(
|
||||
member.name,
|
||||
Diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled,
|
||||
);
|
||||
}
|
||||
}
|
||||
return autoValue;
|
||||
return evaluatorResult(autoValue);
|
||||
}
|
||||
|
||||
function computeConstantValue(member: EnumMember): string | number | undefined {
|
||||
function computeConstantEnumMemberValue(member: EnumMember): EvaluatorResult {
|
||||
const isConstEnum = isEnumConst(member.parent);
|
||||
const initializer = member.initializer!;
|
||||
const value = evaluate(initializer, member);
|
||||
if (value !== undefined) {
|
||||
if (isConstEnum && typeof value === "number" && !isFinite(value)) {
|
||||
const result = evaluate(initializer, member);
|
||||
if (result.value !== undefined) {
|
||||
if (isConstEnum && typeof result.value === "number" && !isFinite(result.value)) {
|
||||
error(
|
||||
initializer,
|
||||
isNaN(value) ?
|
||||
isNaN(result.value) ?
|
||||
Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN :
|
||||
Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value,
|
||||
);
|
||||
}
|
||||
else if (getIsolatedModules(compilerOptions) && typeof value === "string" && !isSyntacticallyString(initializer)) {
|
||||
else if (getIsolatedModules(compilerOptions) && typeof result.value === "string" && !result.isSyntacticallyString) {
|
||||
error(
|
||||
initializer,
|
||||
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 {
|
||||
else {
|
||||
checkTypeAssignableTo(checkExpression(initializer), numberType, initializer, Diagnostics.Type_0_is_not_assignable_to_type_1_as_required_for_computed_enum_member_values);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function isSyntacticallyNumericConstant(expr: Expression): boolean {
|
||||
expr = skipOuterExpressions(expr);
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
return isSyntacticallyNumericConstant((expr as PrefixUnaryExpression).operand);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return isSyntacticallyNumericConstant((expr as BinaryExpression).left) && isSyntacticallyNumericConstant((expr as BinaryExpression).right);
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
function evaluateEntityNameExpression(expr: EntityNameExpression, location?: Declaration) {
|
||||
const symbol = resolveEntityName(expr, SymbolFlags.Value, /*ignoreErrors*/ true);
|
||||
if (!symbol) return undefined;
|
||||
if (!symbol) return evaluatorResult(/*value*/ undefined);
|
||||
|
||||
if (expr.kind === SyntaxKind.Identifier) {
|
||||
const identifier = expr;
|
||||
if (isInfinityOrNaNString(identifier.escapedText) && (symbol === getGlobalSymbol(identifier.escapedText, SymbolFlags.Value, /*diagnostic*/ undefined))) {
|
||||
return +(identifier.escapedText);
|
||||
// Technically we resolved a global lib file here, but the decision to treat this as numeric
|
||||
// is more predicated on the fact that the single-file resolution *didn't* resolve to a
|
||||
// different meaning of `Infinity` or `NaN`. Transpilers handle this no problem.
|
||||
return evaluatorResult(+(identifier.escapedText), /*isSyntacticallyString*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46009,9 +46003,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (isConstantVariable(symbol)) {
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (declaration && isVariableDeclaration(declaration) && !declaration.type && declaration.initializer && (!location || declaration !== location && isBlockScopedNameDeclaredBeforeUse(declaration, location))) {
|
||||
return evaluate(declaration.initializer, declaration);
|
||||
const result = evaluate(declaration.initializer, declaration);
|
||||
if (location && getSourceFileOfNode(location) !== getSourceFileOfNode(declaration)) {
|
||||
return evaluatorResult(
|
||||
result.value,
|
||||
/*isSyntacticallyString*/ false,
|
||||
/*resolvedOtherFiles*/ true,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
|
||||
function evaluateElementAccessExpression(expr: ElementAccessExpression, location?: Declaration) {
|
||||
@ -46022,21 +46025,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const name = escapeLeadingUnderscores(expr.argumentExpression.text);
|
||||
const member = rootSymbol.exports!.get(name);
|
||||
if (member) {
|
||||
Debug.assert(getSourceFileOfNode(member.valueDeclaration) === getSourceFileOfNode(rootSymbol.valueDeclaration));
|
||||
return location ? evaluateEnumMember(expr, member, location) : getEnumMemberValue(member.valueDeclaration as EnumMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
return evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
|
||||
function evaluateEnumMember(expr: Expression, symbol: Symbol, location: Declaration) {
|
||||
const declaration = symbol.valueDeclaration;
|
||||
if (!declaration || declaration === location) {
|
||||
error(expr, Diagnostics.Property_0_is_used_before_being_assigned, symbolToString(symbol));
|
||||
return undefined;
|
||||
return evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
if (!isBlockScopedNameDeclaredBeforeUse(declaration, location)) {
|
||||
error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
|
||||
return 0;
|
||||
return evaluatorResult(/*value*/ 0);
|
||||
}
|
||||
return getEnumMemberValue(declaration as EnumMember);
|
||||
}
|
||||
@ -48668,9 +48673,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return nodeLinks[nodeId]?.flags || 0;
|
||||
}
|
||||
|
||||
function getEnumMemberValue(node: EnumMember): string | number | undefined {
|
||||
function getEnumMemberValue(node: EnumMember): EvaluatorResult {
|
||||
computeEnumMemberValues(node.parent);
|
||||
return getNodeLinks(node).enumMemberValue;
|
||||
return getNodeLinks(node).enumMemberValue ?? evaluatorResult(/*value*/ undefined);
|
||||
}
|
||||
|
||||
function canHaveConstantValue(node: Node): node is EnumMember | AccessExpression {
|
||||
@ -48685,7 +48690,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function getConstantValue(node: EnumMember | AccessExpression): string | number | undefined {
|
||||
if (node.kind === SyntaxKind.EnumMember) {
|
||||
return getEnumMemberValue(node);
|
||||
return getEnumMemberValue(node).value;
|
||||
}
|
||||
|
||||
const symbol = getNodeLinks(node).resolvedSymbol;
|
||||
@ -48693,7 +48698,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// inline property\index accesses only for const enums
|
||||
const member = symbol.valueDeclaration as EnumMember;
|
||||
if (isEnumConst(member.parent)) {
|
||||
return getEnumMemberValue(member);
|
||||
return getEnumMemberValue(member).value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49096,6 +49101,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const node = getParseTreeNode(nodeIn, canHaveConstantValue);
|
||||
return node ? getConstantValue(node) : undefined;
|
||||
},
|
||||
getEnumMemberValue: nodeIn => {
|
||||
const node = getParseTreeNode(nodeIn, isEnumMember);
|
||||
return node ? getEnumMemberValue(node) : undefined;
|
||||
},
|
||||
collectLinkedAliases,
|
||||
getReferencedValueDeclaration,
|
||||
getReferencedValueDeclarations,
|
||||
|
||||
@ -1103,6 +1103,7 @@ export const notImplementedResolver: EmitResolver = {
|
||||
isEntityNameVisible: notImplemented,
|
||||
// Returns the constant value this property access resolves to: notImplemented, or 'undefined' for a non-constant
|
||||
getConstantValue: notImplemented,
|
||||
getEnumMemberValue: notImplemented,
|
||||
getReferencedValueDeclaration: notImplemented,
|
||||
getReferencedValueDeclarations: notImplemented,
|
||||
getTypeReferenceSerializationKind: notImplemented,
|
||||
|
||||
@ -122,7 +122,6 @@ import {
|
||||
isSimpleInlineableExpression,
|
||||
isSourceFile,
|
||||
isStatement,
|
||||
isSyntacticallyString,
|
||||
isTemplateLiteral,
|
||||
isTryStatement,
|
||||
JsxOpeningElement,
|
||||
@ -1915,7 +1914,8 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
// we pass false as 'generateNameForComputedPropertyName' for a backward compatibility purposes
|
||||
// old emitter always generate 'expression' part of the name as-is.
|
||||
const name = getExpressionForPropertyName(member, /*generateNameForComputedPropertyName*/ false);
|
||||
const valueExpression = transformEnumMemberDeclarationValue(member);
|
||||
const evaluated = resolver.getEnumMemberValue(member);
|
||||
const valueExpression = transformEnumMemberDeclarationValue(member, evaluated?.value);
|
||||
const innerAssignment = factory.createAssignment(
|
||||
factory.createElementAccessExpression(
|
||||
currentNamespaceContainerName,
|
||||
@ -1923,7 +1923,7 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
),
|
||||
valueExpression,
|
||||
);
|
||||
const outerAssignment = isSyntacticallyString(valueExpression) ?
|
||||
const outerAssignment = typeof evaluated?.value === "string" || evaluated?.isSyntacticallyString ?
|
||||
innerAssignment :
|
||||
factory.createAssignment(
|
||||
factory.createElementAccessExpression(
|
||||
@ -1948,12 +1948,11 @@ export function transformTypeScript(context: TransformationContext) {
|
||||
*
|
||||
* @param member The enum member node.
|
||||
*/
|
||||
function transformEnumMemberDeclarationValue(member: EnumMember): Expression {
|
||||
const value = resolver.getConstantValue(member);
|
||||
if (value !== undefined) {
|
||||
return typeof value === "string" ? factory.createStringLiteral(value) :
|
||||
value < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-value)) :
|
||||
factory.createNumericLiteral(value);
|
||||
function transformEnumMemberDeclarationValue(member: EnumMember, constantValue: string | number | undefined): Expression {
|
||||
if (constantValue !== undefined) {
|
||||
return typeof constantValue === "string" ? factory.createStringLiteral(constantValue) :
|
||||
constantValue < 0 ? factory.createPrefixUnaryExpression(SyntaxKind.MinusToken, factory.createNumericLiteral(-constantValue)) :
|
||||
factory.createNumericLiteral(constantValue);
|
||||
}
|
||||
else {
|
||||
enableSubstitutionForNonQualifiedEnumMembers();
|
||||
|
||||
@ -5638,6 +5638,7 @@ export interface EmitResolver {
|
||||
isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult;
|
||||
// Returns the constant value this property access resolves to, or 'undefined' for a non-constant
|
||||
getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): string | number | undefined;
|
||||
getEnumMemberValue(node: EnumMember): EvaluatorResult | undefined;
|
||||
getReferencedValueDeclaration(reference: Identifier): Declaration | undefined;
|
||||
getReferencedValueDeclarations(reference: Identifier): Declaration[] | undefined;
|
||||
getTypeReferenceSerializationKind(typeName: EntityName, location?: Node): TypeReferenceSerializationKind;
|
||||
@ -5969,6 +5970,13 @@ export const enum NodeCheckFlags {
|
||||
InCheckIdentifier = 1 << 22,
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export interface EvaluatorResult<T extends string | number | undefined = string | number | undefined> {
|
||||
value: T;
|
||||
isSyntacticallyString: boolean;
|
||||
resolvedOtherFiles: boolean;
|
||||
}
|
||||
|
||||
// dprint-ignore
|
||||
/** @internal */
|
||||
export interface NodeLinks {
|
||||
@ -5979,7 +5987,7 @@ export interface NodeLinks {
|
||||
resolvedSymbol?: Symbol; // Cached name resolution result
|
||||
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
|
||||
effectsSignature?: Signature; // Signature with possible control flow effects
|
||||
enumMemberValue?: string | number; // Constant value of enum member
|
||||
enumMemberValue?: EvaluatorResult; // Constant value of enum member
|
||||
isVisible?: boolean; // Is this node visible
|
||||
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference
|
||||
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
|
||||
@ -10086,6 +10094,6 @@ export interface Queue<T> {
|
||||
|
||||
/** @internal */
|
||||
export interface EvaluationResolver {
|
||||
evaluateEntityNameExpression(expr: EntityNameExpression, location: Declaration | undefined): string | number | undefined;
|
||||
evaluateElementAccessExpression(expr: ElementAccessExpression, location: Declaration | undefined): string | number | undefined;
|
||||
evaluateEntityNameExpression(expr: EntityNameExpression, location: Declaration | undefined): EvaluatorResult;
|
||||
evaluateElementAccessExpression(expr: ElementAccessExpression, location: Declaration | undefined): EvaluatorResult;
|
||||
}
|
||||
|
||||
@ -120,6 +120,7 @@ import {
|
||||
equateValues,
|
||||
escapeLeadingUnderscores,
|
||||
EvaluationResolver,
|
||||
EvaluatorResult,
|
||||
every,
|
||||
ExportAssignment,
|
||||
ExportDeclaration,
|
||||
@ -10649,91 +10650,99 @@ export function getNameFromImportAttribute(node: ImportAttribute) {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function isSyntacticallyString(expr: Expression): boolean {
|
||||
expr = skipOuterExpressions(expr);
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
const left = (expr as BinaryExpression).left;
|
||||
const right = (expr as BinaryExpression).right;
|
||||
return (
|
||||
(expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken &&
|
||||
(isSyntacticallyString(left) || isSyntacticallyString(right))
|
||||
);
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
export function evaluatorResult<T extends string | number | undefined>(value: T, isSyntacticallyString = false, resolvedOtherFiles = false): EvaluatorResult<T> {
|
||||
return { value, isSyntacticallyString, resolvedOtherFiles };
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export function createEvaluator({ evaluateElementAccessExpression, evaluateEntityNameExpression }: EvaluationResolver) {
|
||||
function evaluate(expr: TemplateExpression, location?: Declaration): string;
|
||||
function evaluate(expr: Expression, location?: Declaration): string | number | undefined;
|
||||
function evaluate(expr: Expression, location?: Declaration): string | number | undefined {
|
||||
function evaluate(expr: TemplateExpression, location?: Declaration): EvaluatorResult<string | undefined>;
|
||||
function evaluate(expr: Expression, location?: Declaration): EvaluatorResult;
|
||||
function evaluate(expr: Expression, location?: Declaration): EvaluatorResult {
|
||||
let isSyntacticallyString = false;
|
||||
let resolvedOtherFiles = false;
|
||||
// It's unclear when/whether we should consider skipping other kinds of outer expressions.
|
||||
// Type assertions intentionally break evaluation when evaluating literal types, such as:
|
||||
// type T = `one ${"two" as any} three`; // string
|
||||
// But it's less clear whether such an assertion should break enum member evaluation:
|
||||
// enum E {
|
||||
// A = "one" as any
|
||||
// }
|
||||
// SatisfiesExpressions and non-null assertions seem to have even less reason to break
|
||||
// emitting enum members as literals. However, these expressions also break Babel's
|
||||
// evaluation (but not esbuild's), and the isolatedModules errors we give depend on
|
||||
// our evaluation results, so we're currently being conservative so as to issue errors
|
||||
// on code that might break Babel.
|
||||
expr = skipParentheses(expr);
|
||||
switch (expr.kind) {
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
const value = evaluate((expr as PrefixUnaryExpression).operand, location);
|
||||
if (typeof value === "number") {
|
||||
const result = evaluate((expr as PrefixUnaryExpression).operand, location);
|
||||
resolvedOtherFiles = result.resolvedOtherFiles;
|
||||
if (typeof result.value === "number") {
|
||||
switch ((expr as PrefixUnaryExpression).operator) {
|
||||
case SyntaxKind.PlusToken:
|
||||
return value;
|
||||
return evaluatorResult(result.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.MinusToken:
|
||||
return -value;
|
||||
return evaluatorResult(-result.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.TildeToken:
|
||||
return ~value;
|
||||
return evaluatorResult(~result.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.BinaryExpression: {
|
||||
const left = evaluate((expr as BinaryExpression).left, location);
|
||||
const right = evaluate((expr as BinaryExpression).right, location);
|
||||
if (typeof left === "number" && typeof right === "number") {
|
||||
isSyntacticallyString = (left.isSyntacticallyString || right.isSyntacticallyString) && (expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken;
|
||||
resolvedOtherFiles = left.resolvedOtherFiles || right.resolvedOtherFiles;
|
||||
if (typeof left.value === "number" && typeof right.value === "number") {
|
||||
switch ((expr as BinaryExpression).operatorToken.kind) {
|
||||
case SyntaxKind.BarToken:
|
||||
return left | right;
|
||||
return evaluatorResult(left.value | right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.AmpersandToken:
|
||||
return left & right;
|
||||
return evaluatorResult(left.value & right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.GreaterThanGreaterThanToken:
|
||||
return left >> right;
|
||||
return evaluatorResult(left.value >> right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
||||
return left >>> right;
|
||||
return evaluatorResult(left.value >>> right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.LessThanLessThanToken:
|
||||
return left << right;
|
||||
return evaluatorResult(left.value << right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.CaretToken:
|
||||
return left ^ right;
|
||||
return evaluatorResult(left.value ^ right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.AsteriskToken:
|
||||
return left * right;
|
||||
return evaluatorResult(left.value * right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.SlashToken:
|
||||
return left / right;
|
||||
return evaluatorResult(left.value / right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.PlusToken:
|
||||
return left + right;
|
||||
return evaluatorResult(left.value + right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.MinusToken:
|
||||
return left - right;
|
||||
return evaluatorResult(left.value - right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.PercentToken:
|
||||
return left % right;
|
||||
return evaluatorResult(left.value % right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
case SyntaxKind.AsteriskAsteriskToken:
|
||||
return left ** right;
|
||||
return evaluatorResult(left.value ** right.value, isSyntacticallyString, resolvedOtherFiles);
|
||||
}
|
||||
}
|
||||
else if (
|
||||
(typeof left === "string" || typeof left === "number") &&
|
||||
(typeof right === "string" || typeof right === "number") &&
|
||||
(typeof left.value === "string" || typeof left.value === "number") &&
|
||||
(typeof right.value === "string" || typeof right.value === "number") &&
|
||||
(expr as BinaryExpression).operatorToken.kind === SyntaxKind.PlusToken
|
||||
) {
|
||||
return "" + left + right;
|
||||
return evaluatorResult(
|
||||
"" + left.value + right.value,
|
||||
isSyntacticallyString,
|
||||
resolvedOtherFiles,
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return (expr as StringLiteralLike).text;
|
||||
return evaluatorResult((expr as StringLiteralLike).text, /*isSyntacticallyString*/ true);
|
||||
case SyntaxKind.TemplateExpression:
|
||||
return evaluateTemplateExpression(expr as TemplateExpression, location);
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return +(expr as NumericLiteral).text;
|
||||
case SyntaxKind.ParenthesizedExpression:
|
||||
return evaluate((expr as ParenthesizedExpression).expression, location);
|
||||
return evaluatorResult(+(expr as NumericLiteral).text);
|
||||
case SyntaxKind.Identifier:
|
||||
return evaluateEntityNameExpression(expr as Identifier, location);
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
@ -10744,20 +10753,26 @@ export function createEvaluator({ evaluateElementAccessExpression, evaluateEntit
|
||||
case SyntaxKind.ElementAccessExpression:
|
||||
return evaluateElementAccessExpression(expr as ElementAccessExpression, location);
|
||||
}
|
||||
return undefined;
|
||||
return evaluatorResult(/*value*/ undefined, isSyntacticallyString, resolvedOtherFiles);
|
||||
}
|
||||
|
||||
function evaluateTemplateExpression(expr: TemplateExpression, location?: Declaration) {
|
||||
function evaluateTemplateExpression(expr: TemplateExpression, location?: Declaration): EvaluatorResult<string | undefined> {
|
||||
let result = expr.head.text;
|
||||
let resolvedOtherFiles = false;
|
||||
for (const span of expr.templateSpans) {
|
||||
const value = evaluate(span.expression, location);
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
const spanResult = evaluate(span.expression, location);
|
||||
if (spanResult.value === undefined) {
|
||||
return evaluatorResult(/*value*/ undefined, /*isSyntacticallyString*/ true);
|
||||
}
|
||||
result += value;
|
||||
result += spanResult.value;
|
||||
result += span.literal.text;
|
||||
resolvedOtherFiles ||= spanResult.resolvedOtherFiles;
|
||||
}
|
||||
return result;
|
||||
return evaluatorResult(
|
||||
result,
|
||||
/*isSyntacticallyString*/ true,
|
||||
resolvedOtherFiles,
|
||||
);
|
||||
}
|
||||
return evaluate;
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
computedEnumMemberSyntacticallyString.ts(4,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(5,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(6,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(7,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(8,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(9,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(12,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
|
||||
==== computedEnumMemberSyntacticallyString.ts (5 errors) ====
|
||||
==== computedEnumMemberSyntacticallyString.ts (6 errors) ====
|
||||
const BAR = 2..toFixed(0);
|
||||
|
||||
enum Foo {
|
||||
@ -18,11 +19,18 @@ computedEnumMemberSyntacticallyString.ts(8,9): error TS18033: Type 'string' is n
|
||||
C = (`${BAR}`),
|
||||
~~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
D = (`${BAR}}`) as string,
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
F = BAR,
|
||||
~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
E = `${BAR}`!,
|
||||
~~~~~~~~~
|
||||
G = 2 + BAR,
|
||||
~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
J = H
|
||||
}
|
||||
|
||||
@ -7,8 +7,13 @@ enum Foo {
|
||||
A = `${BAR}`,
|
||||
B = "2" + BAR,
|
||||
C = (`${BAR}`),
|
||||
D = (`${BAR}}`) as string,
|
||||
E = `${BAR}`!,
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +24,9 @@ var Foo;
|
||||
Foo["A"] = `${BAR}`;
|
||||
Foo["B"] = "2" + BAR;
|
||||
Foo["C"] = (`${BAR}`);
|
||||
Foo["D"] = (`${BAR}}`);
|
||||
Foo["E"] = `${BAR}`;
|
||||
Foo[Foo["F"] = BAR] = "F";
|
||||
Foo[Foo["G"] = 2 + BAR] = "G";
|
||||
Foo["H"] = Foo.A;
|
||||
Foo["I"] = Foo.H + BAR;
|
||||
Foo["J"] = Foo.H;
|
||||
})(Foo || (Foo = {}));
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
computedEnumMemberSyntacticallyString.ts(4,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(5,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(6,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(7,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(8,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(9,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
computedEnumMemberSyntacticallyString.ts(12,9): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
|
||||
==== computedEnumMemberSyntacticallyString.ts (5 errors) ====
|
||||
==== computedEnumMemberSyntacticallyString.ts (6 errors) ====
|
||||
const BAR = 2..toFixed(0);
|
||||
|
||||
enum Foo {
|
||||
@ -18,11 +19,18 @@ computedEnumMemberSyntacticallyString.ts(8,9): error TS18033: Type 'string' is n
|
||||
C = (`${BAR}`),
|
||||
~~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
D = (`${BAR}}`) as string,
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
F = BAR,
|
||||
~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
E = `${BAR}`!,
|
||||
~~~~~~~~~
|
||||
G = 2 + BAR,
|
||||
~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
J = H
|
||||
}
|
||||
|
||||
@ -7,8 +7,13 @@ enum Foo {
|
||||
A = `${BAR}`,
|
||||
B = "2" + BAR,
|
||||
C = (`${BAR}`),
|
||||
D = (`${BAR}}`) as string,
|
||||
E = `${BAR}`!,
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +24,9 @@ var Foo;
|
||||
Foo["A"] = `${BAR}`;
|
||||
Foo["B"] = "2" + BAR;
|
||||
Foo["C"] = (`${BAR}`);
|
||||
Foo["D"] = (`${BAR}}`);
|
||||
Foo["E"] = `${BAR}`;
|
||||
Foo[Foo["F"] = BAR] = "F";
|
||||
Foo[Foo["G"] = 2 + BAR] = "G";
|
||||
Foo["H"] = Foo.A;
|
||||
Foo["I"] = Foo.H + BAR;
|
||||
Foo["J"] = Foo.H;
|
||||
})(Foo || (Foo = {}));
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
foo.ts(11,8): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
foo.ts(12,8): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
|
||||
==== ./foo.ts (2 errors) ====
|
||||
import { BAR } from './bar';
|
||||
const LOCAL = 'LOCAL';
|
||||
|
||||
enum Foo {
|
||||
A = `${BAR}`,
|
||||
|
||||
B = LOCAL,
|
||||
C = B,
|
||||
D = C + 'BAR',
|
||||
|
||||
E1 = (`${BAR}`) as string, // We could recognize these,
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
E2 = `${BAR}`!, // but Babel doesn't
|
||||
~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
==== ./bar.ts (0 errors) ====
|
||||
export const BAR = 'bar';
|
||||
@ -2,7 +2,25 @@
|
||||
|
||||
//// [foo.ts]
|
||||
import { BAR } from './bar';
|
||||
enum Foo { A = `${BAR}` }
|
||||
const LOCAL = 'LOCAL';
|
||||
|
||||
enum Foo {
|
||||
A = `${BAR}`,
|
||||
|
||||
B = LOCAL,
|
||||
C = B,
|
||||
D = C + 'BAR',
|
||||
|
||||
E1 = (`${BAR}`) as string, // We could recognize these,
|
||||
E2 = `${BAR}`!, // but Babel doesn't
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
//// [bar.ts]
|
||||
export const BAR = 'bar';
|
||||
@ -11,7 +29,18 @@ export const BAR = 'bar';
|
||||
export const BAR = 'bar';
|
||||
//// [foo.js]
|
||||
import { BAR } from './bar';
|
||||
const LOCAL = 'LOCAL';
|
||||
var Foo;
|
||||
(function (Foo) {
|
||||
Foo["A"] = "bar";
|
||||
Foo["B"] = "LOCAL";
|
||||
Foo["C"] = "LOCAL";
|
||||
Foo["D"] = "LOCALBAR";
|
||||
Foo[Foo["E1"] = (`${BAR}`)] = "E1";
|
||||
Foo[Foo["E2"] = `${BAR}`] = "E2";
|
||||
Foo["F"] = "bar";
|
||||
Foo["G"] = "2bar";
|
||||
Foo["H"] = "bar";
|
||||
Foo["I"] = "barbar";
|
||||
Foo["J"] = "bar";
|
||||
})(Foo || (Foo = {}));
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
foo.ts(11,8): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
foo.ts(12,8): error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
foo.ts(14,7): error TS18055: 'Foo.F' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.
|
||||
foo.ts(15,7): error TS18055: 'Foo.G' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.
|
||||
|
||||
|
||||
==== ./foo.ts (4 errors) ====
|
||||
import { BAR } from './bar';
|
||||
const LOCAL = 'LOCAL';
|
||||
|
||||
enum Foo {
|
||||
A = `${BAR}`,
|
||||
|
||||
B = LOCAL,
|
||||
C = B,
|
||||
D = C + 'BAR',
|
||||
|
||||
E1 = (`${BAR}`) as string, // We could recognize these,
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
E2 = `${BAR}`!, // but Babel doesn't
|
||||
~~~~~~~~~
|
||||
!!! error TS18033: Type 'string' is not assignable to type 'number' as required for computed enum member values.
|
||||
|
||||
F = BAR,
|
||||
~~~
|
||||
!!! error TS18055: 'Foo.F' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.
|
||||
G = 2 + BAR,
|
||||
~~~~~~~
|
||||
!!! error TS18055: 'Foo.G' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
==== ./bar.ts (0 errors) ====
|
||||
export const BAR = 'bar';
|
||||
@ -2,7 +2,25 @@
|
||||
|
||||
//// [foo.ts]
|
||||
import { BAR } from './bar';
|
||||
enum Foo { A = `${BAR}` }
|
||||
const LOCAL = 'LOCAL';
|
||||
|
||||
enum Foo {
|
||||
A = `${BAR}`,
|
||||
|
||||
B = LOCAL,
|
||||
C = B,
|
||||
D = C + 'BAR',
|
||||
|
||||
E1 = (`${BAR}`) as string, // We could recognize these,
|
||||
E2 = `${BAR}`!, // but Babel doesn't
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
//// [bar.ts]
|
||||
export const BAR = 'bar';
|
||||
@ -11,7 +29,18 @@ export const BAR = 'bar';
|
||||
export const BAR = 'bar';
|
||||
//// [foo.js]
|
||||
import { BAR } from './bar';
|
||||
const LOCAL = 'LOCAL';
|
||||
var Foo;
|
||||
(function (Foo) {
|
||||
Foo["A"] = "bar";
|
||||
Foo["B"] = "LOCAL";
|
||||
Foo["C"] = "LOCAL";
|
||||
Foo["D"] = "LOCALBAR";
|
||||
Foo[Foo["E1"] = (`${BAR}`)] = "E1";
|
||||
Foo[Foo["E2"] = `${BAR}`] = "E2";
|
||||
Foo["F"] = "bar";
|
||||
Foo["G"] = "2bar";
|
||||
Foo["H"] = "bar";
|
||||
Foo["I"] = "barbar";
|
||||
Foo["J"] = "bar";
|
||||
})(Foo || (Foo = {}));
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
bad.ts(4,5): error TS18056: Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled.
|
||||
bad.ts(7,5): error TS1061: Enum member must have initializer.
|
||||
|
||||
|
||||
==== ./helpers.ts (0 errors) ====
|
||||
export const foo = 2;
|
||||
|
||||
==== ./bad.ts (1 errors) ====
|
||||
==== ./bad.ts (2 errors) ====
|
||||
import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = foo,
|
||||
b,
|
||||
~
|
||||
!!! error TS18056: Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled.
|
||||
c = 10,
|
||||
d = (c)! satisfies number as any,
|
||||
e,
|
||||
~
|
||||
!!! error TS1061: Enum member must have initializer.
|
||||
}
|
||||
|
||||
==== ./good.ts (0 errors) ====
|
||||
@ -31,4 +37,12 @@ bad.ts(4,5): error TS18056: Enum member following a non-literal numeric member m
|
||||
a = (2),
|
||||
b,
|
||||
}
|
||||
enum E {
|
||||
a,
|
||||
b,
|
||||
c = a,
|
||||
d,
|
||||
e = d | b,
|
||||
f,
|
||||
}
|
||||
|
||||
@ -8,6 +8,9 @@ import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = foo,
|
||||
b,
|
||||
c = 10,
|
||||
d = (c)! satisfies number as any,
|
||||
e,
|
||||
}
|
||||
|
||||
//// [good.ts]
|
||||
@ -28,6 +31,14 @@ enum D {
|
||||
a = (2),
|
||||
b,
|
||||
}
|
||||
enum E {
|
||||
a,
|
||||
b,
|
||||
c = a,
|
||||
d,
|
||||
e = d | b,
|
||||
f,
|
||||
}
|
||||
|
||||
|
||||
//// [helpers.js]
|
||||
@ -43,6 +54,9 @@ var A;
|
||||
(function (A) {
|
||||
A[A["a"] = 2] = "a";
|
||||
A[A["b"] = 3] = "b";
|
||||
A[A["c"] = 10] = "c";
|
||||
A[A["d"] = (A.c)] = "d";
|
||||
A[A["e"] = void 0] = "e";
|
||||
})(A || (A = {}));
|
||||
//// [good.js]
|
||||
"use strict";
|
||||
@ -68,3 +82,12 @@ var D;
|
||||
D[D["a"] = 2] = "a";
|
||||
D[D["b"] = 3] = "b";
|
||||
})(D || (D = {}));
|
||||
var E;
|
||||
(function (E) {
|
||||
E[E["a"] = 0] = "a";
|
||||
E[E["b"] = 1] = "b";
|
||||
E[E["c"] = 0] = "c";
|
||||
E[E["d"] = 1] = "d";
|
||||
E[E["e"] = 1] = "e";
|
||||
E[E["f"] = 2] = "f";
|
||||
})(E || (E = {}));
|
||||
|
||||
@ -8,6 +8,11 @@ enum Foo {
|
||||
A = `${BAR}`,
|
||||
B = "2" + BAR,
|
||||
C = (`${BAR}`),
|
||||
D = (`${BAR}}`) as string,
|
||||
E = `${BAR}`!,
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
@ -4,7 +4,25 @@
|
||||
|
||||
// @filename: ./foo.ts
|
||||
import { BAR } from './bar';
|
||||
enum Foo { A = `${BAR}` }
|
||||
const LOCAL = 'LOCAL';
|
||||
|
||||
enum Foo {
|
||||
A = `${BAR}`,
|
||||
|
||||
B = LOCAL,
|
||||
C = B,
|
||||
D = C + 'BAR',
|
||||
|
||||
E1 = (`${BAR}`) as string, // We could recognize these,
|
||||
E2 = `${BAR}`!, // but Babel doesn't
|
||||
|
||||
F = BAR,
|
||||
G = 2 + BAR,
|
||||
|
||||
H = A,
|
||||
I = H + BAR,
|
||||
J = H
|
||||
}
|
||||
|
||||
// @filename: ./bar.ts
|
||||
export const BAR = 'bar';
|
||||
@ -9,6 +9,9 @@ import { foo } from "./helpers";
|
||||
enum A {
|
||||
a = foo,
|
||||
b,
|
||||
c = 10,
|
||||
d = (c)! satisfies number as any,
|
||||
e,
|
||||
}
|
||||
|
||||
// @filename: ./good.ts
|
||||
@ -29,3 +32,11 @@ enum D {
|
||||
a = (2),
|
||||
b,
|
||||
}
|
||||
enum E {
|
||||
a,
|
||||
b,
|
||||
c = a,
|
||||
d,
|
||||
e = d | b,
|
||||
f,
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user