Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35339,7 +35339,7 @@ namespace ts {
return type;
}

function getQuickTypeOfExpression(node: Expression) {
function getQuickTypeOfExpression(node: Expression): Type | undefined {
let expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true);
if (isJSDocTypeAssertion(expr)) {
const type = getJSDocTypeAssertionType(expr);
Expand All @@ -35348,20 +35348,20 @@ namespace ts {
}
}
expr = skipParentheses(node);
if (isAwaitExpression(expr)) {
const type = getQuickTypeOfExpression(expr.expression);
return type ? getAwaitedType(type) : undefined;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the core of the fix

// Optimize for the common case of a call to a function with a single non-generic call
// signature where we can just fetch the return type without checking the arguments.
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
const type = isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
else if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
if (type) {
return type;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just removed a somewhat redundant lines, replaced that with direct return in the statement above this

}
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
return getTypeFromTypeNode((expr as TypeAssertion).type);
}
else if (node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral ||
node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword) {
else if (isLiteralExpression(node)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a bonus optimization~ (?), we now handle here all literal expressions - instead of just a subset of them

return checkExpression(node);
}
return undefined;
Expand Down
177 changes: 177 additions & 0 deletions tests/baselines/reference/controlFlowIterationErrorsAsync.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(11,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(22,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(34,23): error TS2769: No overload matches this call.
Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'string'.
Type 'number' is not assignable to type 'string'.
Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(45,23): error TS2769: No overload matches this call.
Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'string'.
Type 'number' is not assignable to type 'string'.
Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'.


==== tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts (4 errors) ====
let cond: boolean;

async function len(s: string) {
return s.length;
}

async function f1() {
let x: string | number | boolean;
x = "";
while (cond) {
x = await len(x);
~
!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
!!! error TS2345: Type 'number' is not assignable to type 'string'.
x;
}
x;
}

async function f2() {
let x: string | number | boolean;
x = "";
while (cond) {
x;
x = await len(x);
~
!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
!!! error TS2345: Type 'number' is not assignable to type 'string'.
}
x;
}

declare function foo(x: string): Promise<number>;
declare function foo(x: number): Promise<string>;

async function g1() {
let x: string | number | boolean;
x = "";
while (cond) {
x = await foo(x);
~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'.
!!! error TS2769: Type 'string' is not assignable to type 'number'.
x;
}
x;
}

async function g2() {
let x: string | number | boolean;
x = "";
while (cond) {
x;
x = await foo(x);
~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'.
!!! error TS2769: Type 'string' is not assignable to type 'number'.
}
x;
}

async function asNumber(x: string | number): Promise<number> {
return +x;
}

async function h1() {
let x: string | number | boolean;
x = "0";
while (cond) {
x = +x + 1;
x;
}
}

async function h2() {
let x: string | number | boolean;
x = "0";
while (cond) {
x = await asNumber(x) + 1;
x;
}
}

async function h3() {
let x: string | number | boolean;
x = "0";
while (cond) {
let y = await asNumber(x);
x = y + 1;
x;
}
}

async function h4() {
let x: string | number | boolean;
x = "0";
while (cond) {
x;
let y = await asNumber(x);
x = y + 1;
x;
}
}

// repro #51115

async function get_things(_: number | undefined) {
return [0];
}

async function foobar() {
let before: number | undefined = undefined;
for (let i = 0; i < 2; i++) {
const results = await get_things(before);
before = results[0];
}
}

// repro #43047#issuecomment-821453073

declare function foox(x: string | undefined): Promise<string>

async () => {
let bar: string | undefined = undefined;
do {
const baz = await foox(bar);
bar = baz
} while (bar)
}

// repro #43047#issuecomment-874221939

declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>;

async function myFunc(): Promise<void> {
let lastId: number | undefined = undefined;

while (true) {
const { entities } = await myQuery({
lastId,
});

lastId = entities[entities.length - 1];
}
}

Loading