From 92cd3ae299df987083f2f2c15f088bf97f7d1c24 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 15 Apr 2020 10:34:07 -0700 Subject: [PATCH] No iteration type errors during CFA (#37965) * No iteration type errors during CFA * Add regression test --- src/compiler/checker.ts | 13 +++---- .../reference/noIterationTypeErrorsInCFA.js | 25 +++++++++++++ .../noIterationTypeErrorsInCFA.symbols | 36 ++++++++++++++++++ .../noIterationTypeErrorsInCFA.types | 37 +++++++++++++++++++ .../compiler/noIterationTypeErrorsInCFA.ts | 15 ++++++++ 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/noIterationTypeErrorsInCFA.js create mode 100644 tests/baselines/reference/noIterationTypeErrorsInCFA.symbols create mode 100644 tests/baselines/reference/noIterationTypeErrorsInCFA.types create mode 100644 tests/cases/compiler/noIterationTypeErrorsInCFA.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a0d21dbc49d..a7d45e0ab8c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19793,9 +19793,11 @@ namespace ts { return getTypeOfSymbol(symbol); } if (isVariableDeclaration(declaration) && declaration.parent.parent.kind === SyntaxKind.ForOfStatement) { - const expressionType = getTypeOfDottedName(declaration.parent.parent.expression, /*diagnostic*/ undefined); + const statement = declaration.parent.parent; + const expressionType = getTypeOfDottedName(statement.expression, /*diagnostic*/ undefined); if (expressionType) { - return getForOfIterationType(declaration.parent.parent, expressionType); + const use = statement.awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; + return checkIteratedTypeOrElementType(use, expressionType, undefinedType, /*errorNode*/ undefined); } } if (diagnostic) { @@ -32025,19 +32027,14 @@ namespace ts { } function checkRightHandSideOfForOf(statement: ForOfStatement): Type { - return getForOfIterationType(statement, checkNonNullExpression(statement.expression)); - } - - function getForOfIterationType(statement: ForOfStatement, expressionType: Type) { const use = statement.awaitModifier ? IterationUse.ForAwaitOf : IterationUse.ForOf; - return checkIteratedTypeOrElementType(use, expressionType, undefinedType, statement.expression); + return checkIteratedTypeOrElementType(use, checkNonNullExpression(statement.expression), undefinedType, statement.expression); } function checkIteratedTypeOrElementType(use: IterationUse, inputType: Type, sentType: Type, errorNode: Node | undefined): Type { if (isTypeAny(inputType)) { return inputType; } - return getIteratedTypeOrElementType(use, inputType, sentType, errorNode, /*checkAssignability*/ true) || anyType; } diff --git a/tests/baselines/reference/noIterationTypeErrorsInCFA.js b/tests/baselines/reference/noIterationTypeErrorsInCFA.js new file mode 100644 index 00000000000..20cfca447af --- /dev/null +++ b/tests/baselines/reference/noIterationTypeErrorsInCFA.js @@ -0,0 +1,25 @@ +//// [noIterationTypeErrorsInCFA.ts] +interface F { + d(): void +} +export function doRemove(dds: F | F[]) { + if (!Array.isArray(dds)) { + dds = [dds] + } + for (let n of dds) { + n.d() + } + return dds +} + + +//// [noIterationTypeErrorsInCFA.js] +export function doRemove(dds) { + if (!Array.isArray(dds)) { + dds = [dds]; + } + for (let n of dds) { + n.d(); + } + return dds; +} diff --git a/tests/baselines/reference/noIterationTypeErrorsInCFA.symbols b/tests/baselines/reference/noIterationTypeErrorsInCFA.symbols new file mode 100644 index 00000000000..3498f52d456 --- /dev/null +++ b/tests/baselines/reference/noIterationTypeErrorsInCFA.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/noIterationTypeErrorsInCFA.ts === +interface F { +>F : Symbol(F, Decl(noIterationTypeErrorsInCFA.ts, 0, 0)) + + d(): void +>d : Symbol(F.d, Decl(noIterationTypeErrorsInCFA.ts, 0, 13)) +} +export function doRemove(dds: F | F[]) { +>doRemove : Symbol(doRemove, Decl(noIterationTypeErrorsInCFA.ts, 2, 1)) +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) +>F : Symbol(F, Decl(noIterationTypeErrorsInCFA.ts, 0, 0)) +>F : Symbol(F, Decl(noIterationTypeErrorsInCFA.ts, 0, 0)) + + if (!Array.isArray(dds)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 2 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) + + dds = [dds] +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) + } + for (let n of dds) { +>n : Symbol(n, Decl(noIterationTypeErrorsInCFA.ts, 7, 12)) +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) + + n.d() +>n.d : Symbol(F.d, Decl(noIterationTypeErrorsInCFA.ts, 0, 13)) +>n : Symbol(n, Decl(noIterationTypeErrorsInCFA.ts, 7, 12)) +>d : Symbol(F.d, Decl(noIterationTypeErrorsInCFA.ts, 0, 13)) + } + return dds +>dds : Symbol(dds, Decl(noIterationTypeErrorsInCFA.ts, 3, 25)) +} + diff --git a/tests/baselines/reference/noIterationTypeErrorsInCFA.types b/tests/baselines/reference/noIterationTypeErrorsInCFA.types new file mode 100644 index 00000000000..9d0122627ff --- /dev/null +++ b/tests/baselines/reference/noIterationTypeErrorsInCFA.types @@ -0,0 +1,37 @@ +=== tests/cases/compiler/noIterationTypeErrorsInCFA.ts === +interface F { + d(): void +>d : () => void +} +export function doRemove(dds: F | F[]) { +>doRemove : (dds: F | F[]) => F[] +>dds : F | F[] + + if (!Array.isArray(dds)) { +>!Array.isArray(dds) : boolean +>Array.isArray(dds) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>dds : F | F[] + + dds = [dds] +>dds = [dds] : F[] +>dds : F | F[] +>[dds] : F[] +>dds : F + } + for (let n of dds) { +>n : F +>dds : F[] + + n.d() +>n.d() : void +>n.d : () => void +>n : F +>d : () => void + } + return dds +>dds : F[] +} + diff --git a/tests/cases/compiler/noIterationTypeErrorsInCFA.ts b/tests/cases/compiler/noIterationTypeErrorsInCFA.ts new file mode 100644 index 00000000000..5505eb9b11c --- /dev/null +++ b/tests/cases/compiler/noIterationTypeErrorsInCFA.ts @@ -0,0 +1,15 @@ +// @strict: true +// @target: esnext + +interface F { + d(): void +} +export function doRemove(dds: F | F[]) { + if (!Array.isArray(dds)) { + dds = [dds] + } + for (let n of dds) { + n.d() + } + return dds +}