From 454b4280b11d87c1e043f26d190af0e0f214439c Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Fri, 26 Apr 2019 16:00:04 -0500 Subject: [PATCH] check more case for empty binding patten (#25263) * check more case for empty binding patten * refactor binding pattern checking getWidenedType * fix spelling * fix merge and rebase --- src/compiler/checker.ts | 28 +++++++++--- ...destructuringAssignabilityCheck.errors.txt | 43 +++++++++++++++++++ .../destructuringAssignabilityCheck.js | 32 ++++++++++++++ .../destructuringAssignabilityCheck.symbols | 24 +++++++++++ .../destructuringAssignabilityCheck.types | 40 +++++++++++++++++ .../strictNullEmptyDestructuring.errors.txt | 5 ++- .../destructuringAssignabilityCheck.ts | 17 ++++++++ 7 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/destructuringAssignabilityCheck.errors.txt create mode 100644 tests/baselines/reference/destructuringAssignabilityCheck.js create mode 100644 tests/baselines/reference/destructuringAssignabilityCheck.symbols create mode 100644 tests/baselines/reference/destructuringAssignabilityCheck.types create mode 100644 tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 913d7f3b2a91a..8ee0a4c051996 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26462,14 +26462,28 @@ namespace ts { } // For a binding pattern, validate the initializer and exit if (isBindingPattern(node.name)) { - // Don't validate for-in initializer as it is already an error - if (node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement) { - const initializerType = checkExpressionCached(node.initializer); - if (strictNullChecks && node.name.elements.length === 0) { - checkNonNullNonVoidType(initializerType, node); + const needCheckInitializer = node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement; + const needCheckWidenedType = node.name.elements.length === 0; + if (needCheckInitializer || needCheckWidenedType) { + // Don't validate for-in initializer as it is already an error + const widenedType = getWidenedTypeForVariableLikeDeclaration(node); + if (needCheckInitializer) { + const initializerType = checkExpressionCached(node.initializer!); + if (strictNullChecks && needCheckWidenedType) { + checkNonNullNonVoidType(initializerType, node); + } + else { + checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer); + } } - else { - checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer); + // check the binding pattern with empty elements + if (needCheckWidenedType) { + if (isArrayBindingPattern(node.name)) { + checkIteratedTypeOrElementType(widenedType, node, /* allowStringInput */ false, /* allowAsyncIterables */ false); + } + else if (strictNullChecks) { + checkNonNullNonVoidType(widenedType, node); + } } } return; diff --git a/tests/baselines/reference/destructuringAssignabilityCheck.errors.txt b/tests/baselines/reference/destructuringAssignabilityCheck.errors.txt new file mode 100644 index 0000000000000..bbb4812cf00ed --- /dev/null +++ b/tests/baselines/reference/destructuringAssignabilityCheck.errors.txt @@ -0,0 +1,43 @@ +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(1,7): error TS2461: Type '{}' is not an array type. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(2,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(3,3): error TS2461: Type '{}' is not an array type. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(4,3): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(6,14): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(9,14): error TS2461: Type '{}' is not an array type. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(13,7): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts(15,7): error TS2461: Type '{}' is not an array type. + + +==== tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts (8 errors) ==== + const [] = {}; // should be error + ~~ +!!! error TS2461: Type '{}' is not an array type. + const {} = undefined; // error correctly + ~~ +!!! error TS2532: Object is possibly 'undefined'. + (([]) => 0)({}); // should be error + ~~ +!!! error TS2461: Type '{}' is not an array type. + (({}) => 0)(undefined); // should be error + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + function foo({}: undefined) { + ~~~~~~~~~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + return 0 + } + function bar([]: {}) { + ~~~~~~ +!!! error TS2461: Type '{}' is not an array type. + return 0 + } + + const { }: undefined = 1 + ~~~ +!!! error TS2532: Object is possibly 'undefined'. + + const []: {} = {} + ~~ +!!! error TS2461: Type '{}' is not an array type. + \ No newline at end of file diff --git a/tests/baselines/reference/destructuringAssignabilityCheck.js b/tests/baselines/reference/destructuringAssignabilityCheck.js new file mode 100644 index 0000000000000..2702d8b7f5c59 --- /dev/null +++ b/tests/baselines/reference/destructuringAssignabilityCheck.js @@ -0,0 +1,32 @@ +//// [destructuringAssignabilityCheck.ts] +const [] = {}; // should be error +const {} = undefined; // error correctly +(([]) => 0)({}); // should be error +(({}) => 0)(undefined); // should be error + +function foo({}: undefined) { + return 0 +} +function bar([]: {}) { + return 0 +} + +const { }: undefined = 1 + +const []: {} = {} + + +//// [destructuringAssignabilityCheck.js] +"use strict"; +var _a = {}; // should be error +var _b = undefined; // error correctly +(function (_a) { return 0; })({}); // should be error +(function (_a) { return 0; })(undefined); // should be error +function foo(_a) { + return 0; +} +function bar(_a) { + return 0; +} +var _c = 1; +var _d = {}; diff --git a/tests/baselines/reference/destructuringAssignabilityCheck.symbols b/tests/baselines/reference/destructuringAssignabilityCheck.symbols new file mode 100644 index 0000000000000..c9465f0e41aea --- /dev/null +++ b/tests/baselines/reference/destructuringAssignabilityCheck.symbols @@ -0,0 +1,24 @@ +=== tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts === +const [] = {}; // should be error +const {} = undefined; // error correctly +>undefined : Symbol(undefined) + +(([]) => 0)({}); // should be error +(({}) => 0)(undefined); // should be error +>undefined : Symbol(undefined) + +function foo({}: undefined) { +>foo : Symbol(foo, Decl(destructuringAssignabilityCheck.ts, 3, 23)) + + return 0 +} +function bar([]: {}) { +>bar : Symbol(bar, Decl(destructuringAssignabilityCheck.ts, 7, 1)) + + return 0 +} + +const { }: undefined = 1 + +const []: {} = {} + diff --git a/tests/baselines/reference/destructuringAssignabilityCheck.types b/tests/baselines/reference/destructuringAssignabilityCheck.types new file mode 100644 index 0000000000000..df4467cce6433 --- /dev/null +++ b/tests/baselines/reference/destructuringAssignabilityCheck.types @@ -0,0 +1,40 @@ +=== tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts === +const [] = {}; // should be error +>{} : {} + +const {} = undefined; // error correctly +>undefined : undefined + +(([]) => 0)({}); // should be error +>(([]) => 0)({}) : number +>(([]) => 0) : ([]: {}) => number +>([]) => 0 : ([]: {}) => number +>0 : 0 +>{} : {} + +(({}) => 0)(undefined); // should be error +>(({}) => 0)(undefined) : number +>(({}) => 0) : ({}: undefined) => number +>({}) => 0 : ({}: undefined) => number +>0 : 0 +>undefined : undefined + +function foo({}: undefined) { +>foo : ({}: undefined) => number + + return 0 +>0 : 0 +} +function bar([]: {}) { +>bar : ([]: {}) => number + + return 0 +>0 : 0 +} + +const { }: undefined = 1 +>1 : 1 + +const []: {} = {} +>{} : {} + diff --git a/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt b/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt index 2406ac27d98a1..cb50303d3500d 100644 --- a/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt +++ b/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt @@ -1,3 +1,4 @@ +tests/cases/compiler/strictNullEmptyDestructuring.ts(3,5): error TS2461: Type 'null' is not an array type. tests/cases/compiler/strictNullEmptyDestructuring.ts(3,5): error TS2531: Object is possibly 'null'. tests/cases/compiler/strictNullEmptyDestructuring.ts(5,5): error TS2531: Object is possibly 'null'. tests/cases/compiler/strictNullEmptyDestructuring.ts(7,2): error TS2531: Object is possibly 'null'. @@ -11,11 +12,13 @@ tests/cases/compiler/strictNullEmptyDestructuring.ts(21,5): error TS2533: Object tests/cases/compiler/strictNullEmptyDestructuring.ts(23,2): error TS2533: Object is possibly 'null' or 'undefined'. -==== tests/cases/compiler/strictNullEmptyDestructuring.ts (11 errors) ==== +==== tests/cases/compiler/strictNullEmptyDestructuring.ts (12 errors) ==== // Repro from #20873 let [] = null; ~~ +!!! error TS2461: Type 'null' is not an array type. + ~~ !!! error TS2531: Object is possibly 'null'. let { } = null; diff --git a/tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts b/tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts new file mode 100644 index 0000000000000..af595c1621f96 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/destructuringAssignabilityCheck.ts @@ -0,0 +1,17 @@ +// @strict: true + +const [] = {}; // should be error +const {} = undefined; // error correctly +(([]) => 0)({}); // should be error +(({}) => 0)(undefined); // should be error + +function foo({}: undefined) { + return 0 +} +function bar([]: {}) { + return 0 +} + +const { }: undefined = 1 + +const []: {} = {}