Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 9 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44918,8 +44918,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// check private/protected variable access
const parent = node.parent.parent;
const parentCheckMode = node.dotDotDotToken ? CheckMode.RestBindingElement : CheckMode.Normal;
const parentType = getTypeForBindingElementParent(parent, parentCheckMode);
const name = node.propertyName || node.name;

let parentType = getTypeForBindingElementParent(parent, parentCheckMode);
if (parentType && !parent.initializer) {
parentType = checkNonNullType(parentType, parent);
}

if (parentType && !isBindingPattern(name)) {
const exprType = getLiteralTypeFromPropertyName(name);
if (isTypeUsableAsPropertyName(exprType)) {
Expand Down Expand Up @@ -44951,18 +44956,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isInAmbientOrTypeNode(node)) {
return;
}
const needCheckInitializer = hasOnlyExpressionInitializer(node) && node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement;
const needCheckInitializer = node.initializer && node.parent.parent.kind !== SyntaxKind.ForInStatement;
const needCheckWidenedType = !some(node.name.elements, not(isOmittedExpression));
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);
checkNonNullNonVoidType(checkExpressionCached(node.initializer), node);
}
else {
checkTypeAssignableToAndOptionallyElaborate(initializerType, getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer);
checkTypeAssignableToAndOptionallyElaborate(checkNonNullExpression(node.initializer), getWidenedTypeForVariableLikeDeclaration(node), node, node.initializer);
}
}
// check the binding pattern with empty elements
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
awaitUsingDeclarations.5.ts(3,17): error TS1492: 'await using' declarations may not have binding patterns.
awaitUsingDeclarations.5.ts(3,17): error TS2488: Type 'null' must have a '[Symbol.iterator]()' method that returns an iterator.
awaitUsingDeclarations.5.ts(3,23): error TS18050: The value 'null' cannot be used here.


==== awaitUsingDeclarations.5.ts (2 errors) ====
==== awaitUsingDeclarations.5.ts (3 errors) ====
{
await using a = null,
[b] = null,
~~~
!!! error TS1492: 'await using' declarations may not have binding patterns.
~~~
!!! error TS2488: Type 'null' must have a '[Symbol.iterator]()' method that returns an iterator.
~~~~
!!! error TS18050: The value 'null' cannot be used here.
c = null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
awaitUsingDeclarations.6.ts(2,17): error TS1492: 'await using' declarations may not have binding patterns.
awaitUsingDeclarations.6.ts(2,18): error TS2339: Property 'a' does not exist on type 'null'.
awaitUsingDeclarations.6.ts(2,23): error TS18050: The value 'null' cannot be used here.


==== awaitUsingDeclarations.6.ts (2 errors) ====
==== awaitUsingDeclarations.6.ts (3 errors) ====
{
await using {a} = null;
~~~
!!! error TS1492: 'await using' declarations may not have binding patterns.
~
!!! error TS2339: Property 'a' does not exist on type 'null'.
~~~~
!!! error TS18050: The value 'null' cannot be used here.
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
awaitUsingDeclarations.7.ts(2,5): error TS2853: 'await using' statements are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.
awaitUsingDeclarations.7.ts(3,17): error TS1492: 'await using' declarations may not have binding patterns.
awaitUsingDeclarations.7.ts(3,18): error TS2339: Property 'b' does not exist on type 'null'.
awaitUsingDeclarations.7.ts(3,23): error TS18050: The value 'null' cannot be used here.


==== awaitUsingDeclarations.7.ts (3 errors) ====
==== awaitUsingDeclarations.7.ts (4 errors) ====
{
await using a = null,
~~~~~
Expand All @@ -13,5 +14,7 @@ awaitUsingDeclarations.7.ts(3,18): error TS2339: Property 'b' does not exist on
!!! error TS1492: 'await using' declarations may not have binding patterns.
~
!!! error TS2339: Property 'b' does not exist on type 'null'.
~~~~
!!! error TS18050: The value 'null' cannot be used here.
c = null;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
bindingPatternCannotBeOnlyInferenceSource.ts(2,7): error TS2571: Object is of type 'unknown'.
bindingPatternCannotBeOnlyInferenceSource.ts(3,9): error TS2339: Property 'p1' does not exist on type 'unknown'.
bindingPatternCannotBeOnlyInferenceSource.ts(3,16): error TS2571: Object is of type 'unknown'.
bindingPatternCannotBeOnlyInferenceSource.ts(4,7): error TS2461: Type 'unknown' is not an array type.
bindingPatternCannotBeOnlyInferenceSource.ts(4,7): error TS2571: Object is of type 'unknown'.
bindingPatternCannotBeOnlyInferenceSource.ts(5,7): error TS2461: Type 'unknown' is not an array type.
bindingPatternCannotBeOnlyInferenceSource.ts(5,18): error TS2571: Object is of type 'unknown'.


==== bindingPatternCannotBeOnlyInferenceSource.ts (5 errors) ====
==== bindingPatternCannotBeOnlyInferenceSource.ts (7 errors) ====
declare function f<T>(): T;
const {} = f(); // error (only in strictNullChecks)
~~
!!! error TS2571: Object is of type 'unknown'.
const { p1 } = f(); // error
~~
!!! error TS2339: Property 'p1' does not exist on type 'unknown'.
~~~
!!! error TS2571: Object is of type 'unknown'.
Comment on lines 15 to +19
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 gets a little bit noisy because we essentially report the same problem twice.

There are cases in the changed baselines that require the error to be reported on the initializer, like here:

var [a0, a1]: any = undefined;
~~~~~~~~~
!!! error TS18050: The value 'undefined' cannot be used here.
var [a2 = false, a3 = 1]: any = undefined;
~~~~~~~~~
!!! error TS18050: The value 'undefined' cannot be used here.

So if there is desire to limit the amount of reported errors I think it would be good to report consistently them on the initializer at all times. I think handling this differently for different kind of patterns will complicate the code. But if there is a strong belief that this would provide a better DX - I can implement the required changes.

const [] = f(); // error
~~
!!! error TS2461: Type 'unknown' is not an array type.
Expand All @@ -21,6 +25,8 @@ bindingPatternCannotBeOnlyInferenceSource.ts(5,7): error TS2461: Type 'unknown'
const [e1, e2] = f(); // error
~~~~~~~~
!!! error TS2461: Type 'unknown' is not an array type.
~~~
!!! error TS2571: Object is of type 'unknown'.

// Repro from #43605
type Dispatch<A = { type: any; [extraProps: string]: any }> = { <T extends A>(action: T): T };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
bindingPatternWithNullableInitializer1.ts(10,22): error TS18048: 't' is possibly 'undefined'.
bindingPatternWithNullableInitializer1.ts(14,22): error TS18047: 't' is possibly 'null'.
bindingPatternWithNullableInitializer1.ts(18,22): error TS18049: 't' is possibly 'null' or 'undefined'.
bindingPatternWithNullableInitializer1.ts(22,14): error TS2532: Object is possibly 'undefined'.
bindingPatternWithNullableInitializer1.ts(31,18): error TS18048: 'input.a.b' is possibly 'undefined'.


==== bindingPatternWithNullableInitializer1.ts (5 errors) ====
// https://github.com/microsoft/TypeScript/issues/60119

interface T {
a?: unknown;
b?: unknown;
c?: unknown;
}

function f1<L extends keyof T>(k: L, t: T | undefined) {
const { [k]: v } = t;
~
!!! error TS18048: 't' is possibly 'undefined'.
}

function f2<L extends keyof T>(k: L, t: T | null) {
const { [k]: v } = t;
~
!!! error TS18047: 't' is possibly 'null'.
}

function f3<L extends keyof T>(k: L, t: T | null | undefined) {
const { [k]: v } = t;
~
!!! error TS18049: 't' is possibly 'null' or 'undefined'.
}

function f4<L extends keyof T>(k: L, t: { f: T | undefined }) {
const { f: { [k]: v } } = t;
~~~~~~~~~~
!!! error TS2532: Object is possibly 'undefined'.
}

// https://github.com/microsoft/TypeScript/issues/60179

const input: {
a?: { b?: { c: string } };
} = { a: undefined };

const { ...b } = input.a?.b;
~~~~~~~~~~
!!! error TS18048: 'input.a.b' is possibly 'undefined'.

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//// [tests/cases/conformance/es6/destructuring/bindingPatternWithNullableInitializer1.ts] ////

=== bindingPatternWithNullableInitializer1.ts ===
// https://github.com/microsoft/TypeScript/issues/60119

interface T {
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))

a?: unknown;
>a : Symbol(T.a, Decl(bindingPatternWithNullableInitializer1.ts, 2, 13))

b?: unknown;
>b : Symbol(T.b, Decl(bindingPatternWithNullableInitializer1.ts, 3, 14))

c?: unknown;
>c : Symbol(T.c, Decl(bindingPatternWithNullableInitializer1.ts, 4, 14))
}

function f1<L extends keyof T>(k: L, t: T | undefined) {
>f1 : Symbol(f1, Decl(bindingPatternWithNullableInitializer1.ts, 6, 1))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 8, 12))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 8, 31))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 8, 12))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 8, 36))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))

const { [k]: v } = t;
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 8, 31))
>v : Symbol(v, Decl(bindingPatternWithNullableInitializer1.ts, 9, 9))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 8, 36))
}

function f2<L extends keyof T>(k: L, t: T | null) {
>f2 : Symbol(f2, Decl(bindingPatternWithNullableInitializer1.ts, 10, 1))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 12, 12))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 12, 31))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 12, 12))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 12, 36))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))

const { [k]: v } = t;
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 12, 31))
>v : Symbol(v, Decl(bindingPatternWithNullableInitializer1.ts, 13, 9))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 12, 36))
}

function f3<L extends keyof T>(k: L, t: T | null | undefined) {
>f3 : Symbol(f3, Decl(bindingPatternWithNullableInitializer1.ts, 14, 1))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 16, 12))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 16, 31))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 16, 12))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 16, 36))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))

const { [k]: v } = t;
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 16, 31))
>v : Symbol(v, Decl(bindingPatternWithNullableInitializer1.ts, 17, 9))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 16, 36))
}

function f4<L extends keyof T>(k: L, t: { f: T | undefined }) {
>f4 : Symbol(f4, Decl(bindingPatternWithNullableInitializer1.ts, 18, 1))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 20, 12))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 20, 31))
>L : Symbol(L, Decl(bindingPatternWithNullableInitializer1.ts, 20, 12))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 20, 36))
>f : Symbol(f, Decl(bindingPatternWithNullableInitializer1.ts, 20, 41))
>T : Symbol(T, Decl(bindingPatternWithNullableInitializer1.ts, 0, 0))

const { f: { [k]: v } } = t;
>f : Symbol(f, Decl(bindingPatternWithNullableInitializer1.ts, 20, 41))
>k : Symbol(k, Decl(bindingPatternWithNullableInitializer1.ts, 20, 31))
>v : Symbol(v, Decl(bindingPatternWithNullableInitializer1.ts, 21, 14))
>t : Symbol(t, Decl(bindingPatternWithNullableInitializer1.ts, 20, 36))
}

// https://github.com/microsoft/TypeScript/issues/60179

const input: {
>input : Symbol(input, Decl(bindingPatternWithNullableInitializer1.ts, 26, 5))

a?: { b?: { c: string } };
>a : Symbol(a, Decl(bindingPatternWithNullableInitializer1.ts, 26, 14))
>b : Symbol(b, Decl(bindingPatternWithNullableInitializer1.ts, 27, 7))
>c : Symbol(c, Decl(bindingPatternWithNullableInitializer1.ts, 27, 13))

} = { a: undefined };
>a : Symbol(a, Decl(bindingPatternWithNullableInitializer1.ts, 28, 5))
>undefined : Symbol(undefined)

const { ...b } = input.a?.b;
>b : Symbol(b, Decl(bindingPatternWithNullableInitializer1.ts, 30, 7))
>input.a?.b : Symbol(b, Decl(bindingPatternWithNullableInitializer1.ts, 27, 7))
>input.a : Symbol(a, Decl(bindingPatternWithNullableInitializer1.ts, 26, 14))
>input : Symbol(input, Decl(bindingPatternWithNullableInitializer1.ts, 26, 5))
>a : Symbol(a, Decl(bindingPatternWithNullableInitializer1.ts, 26, 14))
>b : Symbol(b, Decl(bindingPatternWithNullableInitializer1.ts, 27, 7))

Loading
Loading