@@ -932,6 +932,18 @@ namespace ts {
932
932
addErrorOrSuggestion(isError, "message" in message ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) : createDiagnosticForNodeFromMessageChain(location, message));
933
933
}
934
934
935
+ function errorAndMaybeSuggestAwait(
936
+ location: Node | undefined,
937
+ maybeMissingAwait: boolean,
938
+ defaultMessage: DiagnosticMessage,
939
+ missingAwaitMessage: DiagnosticMessage,
940
+ arg0?: string | number | undefined, arg1?: string | number | undefined, arg2?: string | number | undefined, arg3?: string | number | undefined): Diagnostic {
941
+ if (maybeMissingAwait) {
942
+ return error(location, missingAwaitMessage, arg0, arg1, arg2, arg3);
943
+ }
944
+ return error(location, defaultMessage, arg0, arg1, arg2, arg3);
945
+ }
946
+
935
947
function createSymbol(flags: SymbolFlags, name: __String, checkFlags?: CheckFlags) {
936
948
symbolCount++;
937
949
const symbol = <TransientSymbol>(new Symbol(flags | SymbolFlags.Transient, name));
@@ -23662,9 +23674,14 @@ namespace ts {
23662
23674
}
23663
23675
}
23664
23676
23665
- function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage): boolean {
23677
+ function checkArithmeticOperandType(operand: Node, type: Type, diagnostic: DiagnosticMessage, missingAwaitDiagnostic: DiagnosticMessage ): boolean {
23666
23678
if (!isTypeAssignableTo(type, numberOrBigIntType)) {
23667
- error(operand, diagnostic);
23679
+ const awaitedType = getAwaitedType(type);
23680
+ errorAndMaybeSuggestAwait(
23681
+ operand,
23682
+ !!awaitedType && isTypeAssignableTo(awaitedType, numberOrBigIntType),
23683
+ diagnostic,
23684
+ missingAwaitDiagnostic);
23668
23685
return false;
23669
23686
}
23670
23687
return true;
@@ -23862,7 +23879,8 @@ namespace ts {
23862
23879
case SyntaxKind.PlusPlusToken:
23863
23880
case SyntaxKind.MinusMinusToken:
23864
23881
const ok = checkArithmeticOperandType(node.operand, checkNonNullType(operandType, node.operand),
23865
- Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
23882
+ Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type,
23883
+ Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type_Did_you_forget_to_use_await);
23866
23884
if (ok) {
23867
23885
// run check only if former checks succeeded to avoid reporting cascading errors
23868
23886
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
@@ -23880,7 +23898,8 @@ namespace ts {
23880
23898
const ok = checkArithmeticOperandType(
23881
23899
node.operand,
23882
23900
checkNonNullType(operandType, node.operand),
23883
- Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
23901
+ Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type,
23902
+ Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type_Did_you_forget_to_use_await);
23884
23903
if (ok) {
23885
23904
// run check only if former checks succeeded to avoid reporting cascading errors
23886
23905
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
@@ -24272,8 +24291,8 @@ namespace ts {
24272
24291
}
24273
24292
else {
24274
24293
// otherwise just check each operand separately and report errors as normal
24275
- const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type);
24276
- const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type);
24294
+ const leftOk = checkArithmeticOperandType(left, leftType, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, Diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type_Did_you_forget_to_use_await );
24295
+ const rightOk = checkArithmeticOperandType(right, rightType, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, Diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type_Did_you_forget_to_use_await );
24277
24296
let resultType: Type;
24278
24297
// If both are any or unknown, allow operation; assume it will resolve to number
24279
24298
if ((isTypeAssignableToKind(leftType, TypeFlags.AnyOrUnknown) && isTypeAssignableToKind(rightType, TypeFlags.AnyOrUnknown)) ||
@@ -24282,7 +24301,7 @@ namespace ts {
24282
24301
) {
24283
24302
resultType = numberType;
24284
24303
}
24285
- // At least one is assignable to bigint, so both should be only assignable to bigint
24304
+ // At least one is assignable to bigint, so check that both are
24286
24305
else if (isTypeAssignableToKind(leftType, TypeFlags.BigIntLike) && isTypeAssignableToKind(rightType, TypeFlags.BigIntLike)) {
24287
24306
switch (operator) {
24288
24307
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
@@ -24291,8 +24310,9 @@ namespace ts {
24291
24310
}
24292
24311
resultType = bigintType;
24293
24312
}
24313
+ // Exactly one of leftType/rightType is assignable to bigint
24294
24314
else {
24295
- reportOperatorError();
24315
+ reportOperatorError((awaitedLeft, awaitedRight) => isTypeAssignableToKind(awaitedLeft, TypeFlags.BigIntLike) && isTypeAssignableToKind(awaitedRight, TypeFlags.BigIntLike) );
24296
24316
resultType = errorType;
24297
24317
}
24298
24318
if (leftOk && rightOk) {
@@ -24337,7 +24357,14 @@ namespace ts {
24337
24357
}
24338
24358
24339
24359
if (!resultType) {
24340
- reportOperatorError();
24360
+ // Types that have a reasonably good chance of being a valid operand type.
24361
+ // If both types have an awaited type of one of these, we’ll assume the user
24362
+ // might be missing an await without doing an exhaustive check that inserting
24363
+ // await(s) will actually be a completely valid binary expression.
24364
+ const closeEnoughKind = TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.AnyOrUnknown;
24365
+ reportOperatorError((awaitedLeft, awaitedRight) =>
24366
+ isTypeAssignableToKind(awaitedLeft, closeEnoughKind) &&
24367
+ isTypeAssignableToKind(awaitedRight, closeEnoughKind));
24341
24368
return anyType;
24342
24369
}
24343
24370
@@ -24352,21 +24379,18 @@ namespace ts {
24352
24379
if (checkForDisallowedESSymbolOperand(operator)) {
24353
24380
leftType = getBaseTypeOfLiteralType(checkNonNullType(leftType, left));
24354
24381
rightType = getBaseTypeOfLiteralType(checkNonNullType(rightType, right));
24355
- if (!(isTypeComparableTo(leftType, rightType) || isTypeComparableTo(rightType, leftType) ||
24356
- (isTypeAssignableTo(leftType, numberOrBigIntType) && isTypeAssignableTo(rightType, numberOrBigIntType))
24357
- )) {
24358
- reportOperatorError();
24359
- }
24382
+ reportOperatorErrorUnless((left, right) =>
24383
+ isTypeComparableTo(left, right) || isTypeComparableTo(right, left) || (
24384
+ isTypeAssignableTo(left, numberOrBigIntType) && isTypeAssignableTo(right, numberOrBigIntType)));
24360
24385
}
24361
24386
return booleanType;
24362
24387
case SyntaxKind.EqualsEqualsToken:
24363
24388
case SyntaxKind.ExclamationEqualsToken:
24364
24389
case SyntaxKind.EqualsEqualsEqualsToken:
24365
24390
case SyntaxKind.ExclamationEqualsEqualsToken:
24366
- if (!isTypeEqualityComparableTo(leftType, rightType) && !isTypeEqualityComparableTo(rightType, leftType)) {
24367
- reportOperatorError();
24368
- }
24391
+ reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
24369
24392
return booleanType;
24393
+
24370
24394
case SyntaxKind.InstanceOfKeyword:
24371
24395
return checkInstanceOfExpression(left, right, leftType, rightType);
24372
24396
case SyntaxKind.InKeyword:
@@ -24493,29 +24517,61 @@ namespace ts {
24493
24517
}
24494
24518
}
24495
24519
24496
- function reportOperatorError() {
24497
- const [leftStr, rightStr] = getTypeNamesForErrorDisplay(leftType, rightType);
24520
+ /**
24521
+ * Returns true if an error is reported
24522
+ */
24523
+ function reportOperatorErrorUnless(typesAreCompatible: (left: Type, right: Type) => boolean): boolean {
24524
+ if (!typesAreCompatible(leftType, rightType)) {
24525
+ reportOperatorError(typesAreCompatible);
24526
+ return true;
24527
+ }
24528
+ return false;
24529
+ }
24530
+
24531
+ function reportOperatorError(awaitedTypesAreCompatible?: (left: Type, right: Type) => boolean) {
24532
+ let wouldWorkWithAwait = false;
24498
24533
const errNode = errorNode || operatorToken;
24499
- if (!tryGiveBetterPrimaryError(errNode, leftStr, rightStr)) {
24500
- error(
24534
+ const [leftStr, rightStr] = getTypeNamesForErrorDisplay(leftType, rightType);
24535
+ if (awaitedTypesAreCompatible) {
24536
+ const awaitedLeftType = getAwaitedType(leftType);
24537
+ const awaitedRightType = getAwaitedType(rightType);
24538
+ wouldWorkWithAwait = !!(awaitedLeftType && awaitedRightType) && awaitedTypesAreCompatible(awaitedLeftType, awaitedRightType);
24539
+ }
24540
+
24541
+ if (!tryGiveBetterPrimaryError(errNode, wouldWorkWithAwait, leftStr, rightStr)) {
24542
+ errorAndMaybeSuggestAwait(
24501
24543
errNode,
24544
+ wouldWorkWithAwait,
24502
24545
Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2,
24546
+ Diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2_Did_you_forget_to_use_await,
24503
24547
tokenToString(operatorToken.kind),
24504
24548
leftStr,
24505
24549
rightStr,
24506
24550
);
24507
24551
}
24508
24552
}
24509
24553
24510
- function tryGiveBetterPrimaryError(errNode: Node, leftStr: string, rightStr: string) {
24554
+ function tryGiveBetterPrimaryError(errNode: Node, maybeMissingAwait: boolean, leftStr: string, rightStr: string) {
24555
+ let typeName: string | undefined;
24511
24556
switch (operatorToken.kind) {
24512
24557
case SyntaxKind.EqualsEqualsEqualsToken:
24513
24558
case SyntaxKind.EqualsEqualsToken:
24514
- return error(errNode, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, "false", leftStr, rightStr);
24559
+ typeName = "true";
24560
+ break;
24515
24561
case SyntaxKind.ExclamationEqualsEqualsToken:
24516
24562
case SyntaxKind.ExclamationEqualsToken:
24517
- return error(errNode, Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap, "true", leftStr, rightStr);
24518
- }
24563
+ typeName = "false";
24564
+ }
24565
+
24566
+ if (typeName) {
24567
+ return errorAndMaybeSuggestAwait(
24568
+ errNode,
24569
+ maybeMissingAwait,
24570
+ Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap,
24571
+ Diagnostics.This_condition_will_always_return_0_since_the_types_1_and_2_have_no_overlap_Did_you_forget_to_use_await,
24572
+ typeName, leftStr, rightStr);
24573
+ }
24574
+
24519
24575
return undefined;
24520
24576
}
24521
24577
}
0 commit comments