From 4acdca5258f895bbca8ee091184131686cddf731 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Wed, 10 Jan 2018 01:46:36 +0000 Subject: [PATCH] Enforce strictNullChecks for RHS of empty destructuring assignment When strictNullChecks is on, check the RHS of the following destructuring assignments for possible null or undefined: const {} = ... const [] = ... let {} = ... let [] = ... ({} = ...) --- src/compiler/checker.ts | 9 +- .../strictNullEmptyDestructuring.errors.txt | 60 +++++++++++++ .../reference/strictNullEmptyDestructuring.js | 39 +++++++++ .../strictNullEmptyDestructuring.symbols | 49 +++++++++++ .../strictNullEmptyDestructuring.types | 87 +++++++++++++++++++ .../compiler/strictNullEmptyDestructuring.ts | 25 ++++++ 6 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/strictNullEmptyDestructuring.errors.txt create mode 100644 tests/baselines/reference/strictNullEmptyDestructuring.js create mode 100644 tests/baselines/reference/strictNullEmptyDestructuring.symbols create mode 100644 tests/baselines/reference/strictNullEmptyDestructuring.types create mode 100644 tests/cases/compiler/strictNullEmptyDestructuring.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16bfd7fa7e2..7a93ad42105 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18908,6 +18908,9 @@ namespace ts { target = (target).left; } if (target.kind === SyntaxKind.ObjectLiteralExpression) { + if (strictNullChecks && (target).properties.length === 0) { + return checkNonNullType(sourceType, target); + } return checkObjectLiteralAssignment(target, sourceType); } if (target.kind === SyntaxKind.ArrayLiteralExpression) { @@ -21833,7 +21836,11 @@ namespace ts { 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) { - checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined); + const initializerType = checkExpressionCached(node.initializer); + if (strictNullChecks && node.name.elements.length === 0) { + checkNonNullType(initializerType, node); + } + checkTypeAssignableTo(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, /*headMessage*/ undefined); checkParameterInitializer(node); } return; diff --git a/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt b/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt new file mode 100644 index 00000000000..2406ac27d98 --- /dev/null +++ b/tests/baselines/reference/strictNullEmptyDestructuring.errors.txt @@ -0,0 +1,60 @@ +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'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(9,5): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(11,2): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(13,5): error TS2531: Object is possibly 'null'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(15,2): error TS2531: Object is possibly 'null'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(17,5): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(19,2): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(21,5): error TS2533: Object is possibly 'null' or 'undefined'. +tests/cases/compiler/strictNullEmptyDestructuring.ts(23,2): error TS2533: Object is possibly 'null' or 'undefined'. + + +==== tests/cases/compiler/strictNullEmptyDestructuring.ts (11 errors) ==== + // Repro from #20873 + + let [] = null; + ~~ +!!! error TS2531: Object is possibly 'null'. + + let { } = null; + ~~~ +!!! error TS2531: Object is possibly 'null'. + + ({} = null); + ~~ +!!! error TS2531: Object is possibly 'null'. + + let { } = undefined; + ~~~ +!!! error TS2532: Object is possibly 'undefined'. + + ({} = undefined); + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + let { } = Math.random() ? {} : null; + ~~~ +!!! error TS2531: Object is possibly 'null'. + + ({} = Math.random() ? {} : null); + ~~ +!!! error TS2531: Object is possibly 'null'. + + let { } = Math.random() ? {} : undefined; + ~~~ +!!! error TS2532: Object is possibly 'undefined'. + + ({} = Math.random() ? {} : undefined); + ~~ +!!! error TS2532: Object is possibly 'undefined'. + + let { } = Math.random() ? null : undefined; + ~~~ +!!! error TS2533: Object is possibly 'null' or 'undefined'. + + ({} = Math.random() ? null : undefined); + ~~ +!!! error TS2533: Object is possibly 'null' or 'undefined'. + \ No newline at end of file diff --git a/tests/baselines/reference/strictNullEmptyDestructuring.js b/tests/baselines/reference/strictNullEmptyDestructuring.js new file mode 100644 index 00000000000..c46b44e21d3 --- /dev/null +++ b/tests/baselines/reference/strictNullEmptyDestructuring.js @@ -0,0 +1,39 @@ +//// [strictNullEmptyDestructuring.ts] +// Repro from #20873 + +let [] = null; + +let { } = null; + +({} = null); + +let { } = undefined; + +({} = undefined); + +let { } = Math.random() ? {} : null; + +({} = Math.random() ? {} : null); + +let { } = Math.random() ? {} : undefined; + +({} = Math.random() ? {} : undefined); + +let { } = Math.random() ? null : undefined; + +({} = Math.random() ? null : undefined); + + +//// [strictNullEmptyDestructuring.js] +// Repro from #20873 +var _a = null; +var _b = null; +(null); +var _c = undefined; +(undefined); +var _d = Math.random() ? {} : null; +(Math.random() ? {} : null); +var _e = Math.random() ? {} : undefined; +(Math.random() ? {} : undefined); +var _f = Math.random() ? null : undefined; +(Math.random() ? null : undefined); diff --git a/tests/baselines/reference/strictNullEmptyDestructuring.symbols b/tests/baselines/reference/strictNullEmptyDestructuring.symbols new file mode 100644 index 00000000000..5968efa4925 --- /dev/null +++ b/tests/baselines/reference/strictNullEmptyDestructuring.symbols @@ -0,0 +1,49 @@ +=== tests/cases/compiler/strictNullEmptyDestructuring.ts === +// Repro from #20873 + +let [] = null; + +let { } = null; + +({} = null); + +let { } = undefined; +>undefined : Symbol(undefined) + +({} = undefined); +>undefined : Symbol(undefined) + +let { } = Math.random() ? {} : null; +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + +({} = Math.random() ? {} : null); +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) + +let { } = Math.random() ? {} : undefined; +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>undefined : Symbol(undefined) + +({} = Math.random() ? {} : undefined); +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>undefined : Symbol(undefined) + +let { } = Math.random() ? null : undefined; +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>undefined : Symbol(undefined) + +({} = Math.random() ? null : undefined); +>Math.random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.d.ts, --, --)) +>undefined : Symbol(undefined) + diff --git a/tests/baselines/reference/strictNullEmptyDestructuring.types b/tests/baselines/reference/strictNullEmptyDestructuring.types new file mode 100644 index 00000000000..10215a0e0d1 --- /dev/null +++ b/tests/baselines/reference/strictNullEmptyDestructuring.types @@ -0,0 +1,87 @@ +=== tests/cases/compiler/strictNullEmptyDestructuring.ts === +// Repro from #20873 + +let [] = null; +>null : null + +let { } = null; +>null : null + +({} = null); +>({} = null) : any +>{} = null : any +>{} : {} +>null : null + +let { } = undefined; +>undefined : undefined + +({} = undefined); +>({} = undefined) : any +>{} = undefined : any +>{} : {} +>undefined : undefined + +let { } = Math.random() ? {} : null; +>Math.random() ? {} : null : {} | null +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>{} : {} +>null : null + +({} = Math.random() ? {} : null); +>({} = Math.random() ? {} : null) : {} +>{} = Math.random() ? {} : null : {} +>{} : {} +>Math.random() ? {} : null : {} | null +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>{} : {} +>null : null + +let { } = Math.random() ? {} : undefined; +>Math.random() ? {} : undefined : {} | undefined +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>{} : {} +>undefined : undefined + +({} = Math.random() ? {} : undefined); +>({} = Math.random() ? {} : undefined) : {} +>{} = Math.random() ? {} : undefined : {} +>{} : {} +>Math.random() ? {} : undefined : {} | undefined +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>{} : {} +>undefined : undefined + +let { } = Math.random() ? null : undefined; +>Math.random() ? null : undefined : null | undefined +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>null : null +>undefined : undefined + +({} = Math.random() ? null : undefined); +>({} = Math.random() ? null : undefined) : any +>{} = Math.random() ? null : undefined : any +>{} : {} +>Math.random() ? null : undefined : null | undefined +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>null : null +>undefined : undefined + diff --git a/tests/cases/compiler/strictNullEmptyDestructuring.ts b/tests/cases/compiler/strictNullEmptyDestructuring.ts new file mode 100644 index 00000000000..97126154182 --- /dev/null +++ b/tests/cases/compiler/strictNullEmptyDestructuring.ts @@ -0,0 +1,25 @@ +// @strictNullChecks: true + +// Repro from #20873 + +let [] = null; + +let { } = null; + +({} = null); + +let { } = undefined; + +({} = undefined); + +let { } = Math.random() ? {} : null; + +({} = Math.random() ? {} : null); + +let { } = Math.random() ? {} : undefined; + +({} = Math.random() ? {} : undefined); + +let { } = Math.random() ? null : undefined; + +({} = Math.random() ? null : undefined);