From f248567dabd1b9cc11bb53d815e79c80becaee51 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 22 Apr 2020 09:56:32 -0700 Subject: [PATCH] Filter undefined only in binding patterns in params (#38116) initialiser. But this is only correct when the initialiser is for a parameter. For example: ```ts declare let x: { s: string } | undefined; const { s } = x; ``` This PR removes undefined from the type of a binding pattern only when the binding pattern's parent is a parameter. This fixes the regression from 3.8. However, it's still not the ideal fix; we should be able to use control flow to solve this problem. Consider: ```ts const { s }: { s: string } | undefined = { s: 'hi' } declare function f({ s }: { s: string } | undefined = { s: 'hi' }): void ``` Neither line should have an error, but the first does in 3.8 and after this change. --- src/compiler/checker.ts | 2 +- ...rInitalizedVariablesFiltersUndefined.errors.txt | 14 ++++++++++++++ ...alTypeForInitalizedVariablesFiltersUndefined.js | 7 ++++++- ...eForInitalizedVariablesFiltersUndefined.symbols | 8 ++++++++ ...ypeForInitalizedVariablesFiltersUndefined.types | 8 ++++++++ ...alTypeForInitalizedVariablesFiltersUndefined.ts | 5 ++++- 6 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 89be1e0aa7..9014e2f43f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7306,7 +7306,7 @@ namespace ts { parentType = getNonNullableType(parentType); } // Filter `undefined` from the type we check against if the parent has an initializer (which handles the `undefined` case implicitly) - else if (strictNullChecks && pattern.parent.initializer) { + else if (strictNullChecks && pattern.parent.initializer && isParameter(pattern.parent)) { parentType = getTypeWithFacts(parentType, TypeFacts.NEUndefined); } diff --git a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.errors.txt b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.errors.txt new file mode 100644 index 0000000000..32ff831d2b --- /dev/null +++ b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts(7,9): error TS2339: Property 's' does not exist on type '{ s: string; } | undefined'. + + +==== tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts (1 errors) ==== + const fInferred = ({ a = 0 } = {}) => a; + // const fInferred: ({ a }?: { a?: number; }) => number + + const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; + + declare var t: { s: string } | undefined; + const { s } = t; + ~ +!!! error TS2339: Property 's' does not exist on type '{ s: string; } | undefined'. + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.js b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.js index 9c2a6eaa78..8311b6ff48 100644 --- a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.js +++ b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.js @@ -2,7 +2,11 @@ const fInferred = ({ a = 0 } = {}) => a; // const fInferred: ({ a }?: { a?: number; }) => number -const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; +const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; + +declare var t: { s: string } | undefined; +const { s } = t; + //// [contextualTypeForInitalizedVariablesFiltersUndefined.js] "use strict"; @@ -15,3 +19,4 @@ var fAnnotated = function (_a) { var _b = (_a === void 0 ? {} : _a).a, a = _b === void 0 ? 0 : _b; return a; }; +var s = t.s; diff --git a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.symbols b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.symbols index 7b67571901..81ae53f941 100644 --- a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.symbols +++ b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.symbols @@ -12,3 +12,11 @@ const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; >a : Symbol(a, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 3, 39)) >a : Symbol(a, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 3, 39)) +declare var t: { s: string } | undefined; +>t : Symbol(t, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 5, 11)) +>s : Symbol(s, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 5, 16)) + +const { s } = t; +>s : Symbol(s, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 6, 7)) +>t : Symbol(t, Decl(contextualTypeForInitalizedVariablesFiltersUndefined.ts, 5, 11)) + diff --git a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.types b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.types index d8278124ff..58580fdacc 100644 --- a/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.types +++ b/tests/baselines/reference/contextualTypeForInitalizedVariablesFiltersUndefined.types @@ -18,3 +18,11 @@ const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; >{} : {} >a : number +declare var t: { s: string } | undefined; +>t : { s: string; } | undefined +>s : string + +const { s } = t; +>s : any +>t : { s: string; } | undefined + diff --git a/tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts b/tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts index 268544f819..cfa8d8d682 100644 --- a/tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts +++ b/tests/cases/compiler/contextualTypeForInitalizedVariablesFiltersUndefined.ts @@ -2,4 +2,7 @@ const fInferred = ({ a = 0 } = {}) => a; // const fInferred: ({ a }?: { a?: number; }) => number -const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; \ No newline at end of file +const fAnnotated: typeof fInferred = ({ a = 0 } = {}) => a; + +declare var t: { s: string } | undefined; +const { s } = t;