mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Suppress error caused by intermediate types in getTypeOfExpression (#54380)
This commit is contained in:
parent
fb7efcfe06
commit
f5ab714d1f
@ -1258,6 +1258,7 @@ export const enum CheckMode {
|
||||
RestBindingElement = 1 << 6, // Checking a type that is going to be used to determine the type of a rest binding element
|
||||
// e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
|
||||
// we need to preserve generic types instead of substituting them for constraints
|
||||
TypeOnly = 1 << 7, // Called from getTypeOfExpression, diagnostics may be omitted
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -36760,7 +36761,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const rightType = getLastResult(state);
|
||||
Debug.assertIsDefined(rightType);
|
||||
|
||||
result = checkBinaryLikeExpressionWorker(node.left, node.operatorToken, node.right, leftType, rightType, node);
|
||||
result = checkBinaryLikeExpressionWorker(node.left, node.operatorToken, node.right, leftType, rightType, state.checkMode, node);
|
||||
}
|
||||
|
||||
state.skip = false;
|
||||
@ -36831,7 +36832,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
const rightType = checkExpression(right, checkMode);
|
||||
return checkBinaryLikeExpressionWorker(left, operatorToken, right, leftType, rightType, errorNode);
|
||||
return checkBinaryLikeExpressionWorker(left, operatorToken, right, leftType, rightType, checkMode, errorNode);
|
||||
}
|
||||
|
||||
function checkBinaryLikeExpressionWorker(
|
||||
@ -36840,6 +36841,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
right: Expression,
|
||||
leftType: Type,
|
||||
rightType: Type,
|
||||
checkMode?: CheckMode,
|
||||
errorNode?: Node
|
||||
): Type {
|
||||
const operator = operatorToken.kind;
|
||||
@ -36993,14 +36995,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
case SyntaxKind.ExclamationEqualsToken:
|
||||
case SyntaxKind.EqualsEqualsEqualsToken:
|
||||
case SyntaxKind.ExclamationEqualsEqualsToken:
|
||||
if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) {
|
||||
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
|
||||
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
|
||||
// We suppress errors in CheckMode.TypeOnly (meaning the invocation came from getTypeOfExpression). During
|
||||
// control flow analysis it is possible for operands to temporarily have narrower types, and those narrower
|
||||
// types may cause the operands to not be comparable. We don't want such errors reported (see #46475).
|
||||
if (!(checkMode && checkMode & CheckMode.TypeOnly)) {
|
||||
if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) {
|
||||
const eqType = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken;
|
||||
error(errorNode, Diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, eqType ? "false" : "true");
|
||||
}
|
||||
checkNaNEquality(errorNode, operator, left, right);
|
||||
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
|
||||
}
|
||||
checkNaNEquality(errorNode, operator, left, right);
|
||||
reportOperatorErrorUnless((left, right) => isTypeEqualityComparableTo(left, right) || isTypeEqualityComparableTo(right, left));
|
||||
return booleanType;
|
||||
|
||||
case SyntaxKind.InstanceOfKeyword:
|
||||
return checkInstanceOfExpression(left, right, leftType, rightType);
|
||||
case SyntaxKind.InKeyword:
|
||||
@ -37355,7 +37361,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
|
||||
const type = checkTruthinessExpression(node.condition);
|
||||
const type = checkTruthinessExpression(node.condition, checkMode);
|
||||
checkTestingKnownTruthyCallableOrAwaitableType(node.condition, type, node.whenTrue);
|
||||
const type1 = checkExpression(node.whenTrue, checkMode);
|
||||
const type2 = checkExpression(node.whenFalse, checkMode);
|
||||
@ -37736,7 +37742,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
}
|
||||
const startInvocationCount = flowInvocationCount;
|
||||
const type = checkExpression(node);
|
||||
const type = checkExpression(node, CheckMode.TypeOnly);
|
||||
// If control flow analysis was required to determine the type, it is worth caching.
|
||||
if (flowInvocationCount !== startInvocationCount) {
|
||||
const cache = flowTypeCache || (flowTypeCache = []);
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
=== tests/cases/conformance/controlFlow/controlFlowNoIntermediateErrors.ts ===
|
||||
// Repros from #46475
|
||||
|
||||
function f1() {
|
||||
>f1 : Symbol(f1, Decl(controlFlowNoIntermediateErrors.ts, 0, 0))
|
||||
|
||||
let code: 0 | 1 | 2 = 0;
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 3, 7))
|
||||
|
||||
const otherCodes: (0 | 1 | 2)[] = [2, 0, 1, 0, 2, 2, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1];
|
||||
>otherCodes : Symbol(otherCodes, Decl(controlFlowNoIntermediateErrors.ts, 4, 9))
|
||||
|
||||
for (const code2 of otherCodes) {
|
||||
>code2 : Symbol(code2, Decl(controlFlowNoIntermediateErrors.ts, 5, 14))
|
||||
>otherCodes : Symbol(otherCodes, Decl(controlFlowNoIntermediateErrors.ts, 4, 9))
|
||||
|
||||
if (code2 === 0) {
|
||||
>code2 : Symbol(code2, Decl(controlFlowNoIntermediateErrors.ts, 5, 14))
|
||||
|
||||
code = code === 2 ? 1 : 0;
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 3, 7))
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 3, 7))
|
||||
}
|
||||
else {
|
||||
code = 2;
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 3, 7))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
>f2 : Symbol(f2, Decl(controlFlowNoIntermediateErrors.ts, 13, 1))
|
||||
|
||||
let code: 0 | 1 = 0;
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 16, 7))
|
||||
|
||||
while (true) {
|
||||
code = code === 1 ? 0 : 1;
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 16, 7))
|
||||
>code : Symbol(code, Decl(controlFlowNoIntermediateErrors.ts, 16, 7))
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
=== tests/cases/conformance/controlFlow/controlFlowNoIntermediateErrors.ts ===
|
||||
// Repros from #46475
|
||||
|
||||
function f1() {
|
||||
>f1 : () => void
|
||||
|
||||
let code: 0 | 1 | 2 = 0;
|
||||
>code : 0 | 1 | 2
|
||||
>0 : 0
|
||||
|
||||
const otherCodes: (0 | 1 | 2)[] = [2, 0, 1, 0, 2, 2, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1];
|
||||
>otherCodes : (0 | 1 | 2)[]
|
||||
>[2, 0, 1, 0, 2, 2, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1] : (0 | 1 | 2)[]
|
||||
>2 : 2
|
||||
>0 : 0
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
>2 : 2
|
||||
>2 : 2
|
||||
>2 : 2
|
||||
>0 : 0
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
>2 : 2
|
||||
>1 : 1
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
>2 : 2
|
||||
>1 : 1
|
||||
|
||||
for (const code2 of otherCodes) {
|
||||
>code2 : 0 | 1 | 2
|
||||
>otherCodes : (0 | 1 | 2)[]
|
||||
|
||||
if (code2 === 0) {
|
||||
>code2 === 0 : boolean
|
||||
>code2 : 0 | 1 | 2
|
||||
>0 : 0
|
||||
|
||||
code = code === 2 ? 1 : 0;
|
||||
>code = code === 2 ? 1 : 0 : 0 | 1
|
||||
>code : 0 | 1 | 2
|
||||
>code === 2 ? 1 : 0 : 0 | 1
|
||||
>code === 2 : boolean
|
||||
>code : 0 | 1 | 2
|
||||
>2 : 2
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
}
|
||||
else {
|
||||
code = 2;
|
||||
>code = 2 : 2
|
||||
>code : 0 | 1 | 2
|
||||
>2 : 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
>f2 : () => void
|
||||
|
||||
let code: 0 | 1 = 0;
|
||||
>code : 0 | 1
|
||||
>0 : 0
|
||||
|
||||
while (true) {
|
||||
>true : true
|
||||
|
||||
code = code === 1 ? 0 : 1;
|
||||
>code = code === 1 ? 0 : 1 : 0 | 1
|
||||
>code : 0 | 1
|
||||
>code === 1 ? 0 : 1 : 0 | 1
|
||||
>code === 1 : boolean
|
||||
>code : 0 | 1
|
||||
>1 : 1
|
||||
>0 : 0
|
||||
>1 : 1
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
// Repros from #46475
|
||||
|
||||
function f1() {
|
||||
let code: 0 | 1 | 2 = 0;
|
||||
const otherCodes: (0 | 1 | 2)[] = [2, 0, 1, 0, 2, 2, 2, 0, 1, 0, 2, 1, 1, 0, 2, 1];
|
||||
for (const code2 of otherCodes) {
|
||||
if (code2 === 0) {
|
||||
code = code === 2 ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
code = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
let code: 0 | 1 = 0;
|
||||
while (true) {
|
||||
code = code === 1 ? 0 : 1;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user