Skip to content

Commit 92d465d

Browse files
committed
Merge pull request microsoft#8730 from Microsoft/destructureOptionalParameter
Fix destructuring of optional parameters with --strictNullChecks
2 parents f1d023a + daafd10 commit 92d465d

File tree

5 files changed

+232
-7
lines changed

5 files changed

+232
-7
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,7 +2745,7 @@ namespace ts {
27452745
// assigned by contextual typing.
27462746
function getTypeForBindingElementParent(node: VariableLikeDeclaration) {
27472747
const symbol = getSymbolOfNode(node);
2748-
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node);
2748+
return symbol && getSymbolLinks(symbol).type || getTypeForVariableLikeDeclaration(node, /*includeOptionality*/ false);
27492749
}
27502750

27512751
function getTextOfPropertyName(name: PropertyName): string {
@@ -2885,7 +2885,7 @@ namespace ts {
28852885
}
28862886

28872887
// Return the inferred type for a variable, parameter, or property declaration
2888-
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration): Type {
2888+
function getTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, includeOptionality: boolean): Type {
28892889
if (declaration.flags & NodeFlags.JavaScriptFile) {
28902890
// If this is a variable in a JavaScript file, then use the JSDoc type (if it has
28912891
// one as its type), otherwise fallback to the below standard TS codepaths to
@@ -2915,7 +2915,7 @@ namespace ts {
29152915

29162916
// Use type from type annotation if one is present
29172917
if (declaration.type) {
2918-
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ !!declaration.questionToken);
2918+
return addOptionality(getTypeFromTypeNode(declaration.type), /*optional*/ declaration.questionToken && includeOptionality);
29192919
}
29202920

29212921
if (declaration.kind === SyntaxKind.Parameter) {
@@ -2937,13 +2937,13 @@ namespace ts {
29372937
? getContextuallyTypedThisType(func)
29382938
: getContextuallyTypedParameterType(<ParameterDeclaration>declaration);
29392939
if (type) {
2940-
return addOptionality(type, /*optional*/ !!declaration.questionToken);
2940+
return addOptionality(type, /*optional*/ declaration.questionToken && includeOptionality);
29412941
}
29422942
}
29432943

29442944
// Use the type of the initializer expression if one is present
29452945
if (declaration.initializer) {
2946-
return addOptionality(checkExpressionCached(declaration.initializer), /*optional*/ !!declaration.questionToken);
2946+
return addOptionality(checkExpressionCached(declaration.initializer), /*optional*/ declaration.questionToken && includeOptionality);
29472947
}
29482948

29492949
// If it is a short-hand property assignment, use the type of the identifier
@@ -3046,7 +3046,7 @@ namespace ts {
30463046
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
30473047
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
30483048
function getWidenedTypeForVariableLikeDeclaration(declaration: VariableLikeDeclaration, reportErrors?: boolean): Type {
3049-
let type = getTypeForVariableLikeDeclaration(declaration);
3049+
let type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ true);
30503050
if (type) {
30513051
if (reportErrors) {
30523052
reportErrorsFromWidening(declaration, type);
@@ -16903,7 +16903,7 @@ namespace ts {
1690316903
}
1690416904

1690516905
if (isBindingPattern(node)) {
16906-
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent);
16906+
return getTypeForVariableLikeDeclaration(<VariableLikeDeclaration>node.parent, /*includeOptionality*/ true);
1690716907
}
1690816908

1690916909
if (isInRightSideOfImportOrExportAssignment(<Identifier>node)) {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//// [destructureOptionalParameter.ts]
2+
3+
declare function f1({ a, b }?: { a: number, b: string }): void;
4+
5+
function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
6+
a;
7+
b;
8+
}
9+
10+
// Repro from #8681
11+
12+
interface Type { t: void }
13+
interface QueryMetadata { q: void }
14+
15+
interface QueryMetadataFactory {
16+
(selector: Type | string, {descendants, read}?: {
17+
descendants?: boolean;
18+
read?: any;
19+
}): ParameterDecorator;
20+
new (selector: Type | string, {descendants, read}?: {
21+
descendants?: boolean;
22+
read?: any;
23+
}): QueryMetadata;
24+
}
25+
26+
27+
//// [destructureOptionalParameter.js]
28+
function f2(_a) {
29+
var _b = _a === void 0 ? { a: 0, b: 0 } : _a, a = _b.a, b = _b.b;
30+
a;
31+
b;
32+
}
33+
34+
35+
//// [destructureOptionalParameter.d.ts]
36+
declare function f1({a, b}?: {
37+
a: number;
38+
b: string;
39+
}): void;
40+
declare function f2({a, b}?: {
41+
a: number;
42+
b: number;
43+
}): void;
44+
interface Type {
45+
t: void;
46+
}
47+
interface QueryMetadata {
48+
q: void;
49+
}
50+
interface QueryMetadataFactory {
51+
(selector: Type | string, {descendants, read}?: {
52+
descendants?: boolean;
53+
read?: any;
54+
}): ParameterDecorator;
55+
new (selector: Type | string, {descendants, read}?: {
56+
descendants?: boolean;
57+
read?: any;
58+
}): QueryMetadata;
59+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
=== tests/cases/compiler/destructureOptionalParameter.ts ===
2+
3+
declare function f1({ a, b }?: { a: number, b: string }): void;
4+
>f1 : Symbol(f1, Decl(destructureOptionalParameter.ts, 0, 0))
5+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 1, 21))
6+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 1, 24))
7+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 1, 32))
8+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 1, 43))
9+
10+
function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
11+
>f2 : Symbol(f2, Decl(destructureOptionalParameter.ts, 1, 63))
12+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 13))
13+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 16))
14+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 23))
15+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 34))
16+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 50))
17+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 56))
18+
19+
a;
20+
>a : Symbol(a, Decl(destructureOptionalParameter.ts, 3, 13))
21+
22+
b;
23+
>b : Symbol(b, Decl(destructureOptionalParameter.ts, 3, 16))
24+
}
25+
26+
// Repro from #8681
27+
28+
interface Type { t: void }
29+
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
30+
>t : Symbol(Type.t, Decl(destructureOptionalParameter.ts, 10, 16))
31+
32+
interface QueryMetadata { q: void }
33+
>QueryMetadata : Symbol(QueryMetadata, Decl(destructureOptionalParameter.ts, 10, 26))
34+
>q : Symbol(QueryMetadata.q, Decl(destructureOptionalParameter.ts, 11, 25))
35+
36+
interface QueryMetadataFactory {
37+
>QueryMetadataFactory : Symbol(QueryMetadataFactory, Decl(destructureOptionalParameter.ts, 11, 35))
38+
39+
(selector: Type | string, {descendants, read}?: {
40+
>selector : Symbol(selector, Decl(destructureOptionalParameter.ts, 14, 5))
41+
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
42+
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 14, 31))
43+
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 14, 43))
44+
45+
descendants?: boolean;
46+
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 14, 53))
47+
48+
read?: any;
49+
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 15, 30))
50+
51+
}): ParameterDecorator;
52+
>ParameterDecorator : Symbol(ParameterDecorator, Decl(lib.d.ts, --, --))
53+
54+
new (selector: Type | string, {descendants, read}?: {
55+
>selector : Symbol(selector, Decl(destructureOptionalParameter.ts, 18, 9))
56+
>Type : Symbol(Type, Decl(destructureOptionalParameter.ts, 6, 1))
57+
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 18, 35))
58+
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 18, 47))
59+
60+
descendants?: boolean;
61+
>descendants : Symbol(descendants, Decl(destructureOptionalParameter.ts, 18, 57))
62+
63+
read?: any;
64+
>read : Symbol(read, Decl(destructureOptionalParameter.ts, 19, 30))
65+
66+
}): QueryMetadata;
67+
>QueryMetadata : Symbol(QueryMetadata, Decl(destructureOptionalParameter.ts, 10, 26))
68+
}
69+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
=== tests/cases/compiler/destructureOptionalParameter.ts ===
2+
3+
declare function f1({ a, b }?: { a: number, b: string }): void;
4+
>f1 : ({a, b}?: { a: number; b: string; } | undefined) => void
5+
>a : number
6+
>b : string
7+
>a : number
8+
>b : string
9+
10+
function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
11+
>f2 : ({a, b}?: { a: number; b: number; }) => void
12+
>a : number
13+
>b : number
14+
>a : number
15+
>b : number
16+
>{ a: 0, b: 0 } : { a: number; b: number; }
17+
>a : number
18+
>0 : number
19+
>b : number
20+
>0 : number
21+
22+
a;
23+
>a : number
24+
25+
b;
26+
>b : number
27+
}
28+
29+
// Repro from #8681
30+
31+
interface Type { t: void }
32+
>Type : Type
33+
>t : void
34+
35+
interface QueryMetadata { q: void }
36+
>QueryMetadata : QueryMetadata
37+
>q : void
38+
39+
interface QueryMetadataFactory {
40+
>QueryMetadataFactory : QueryMetadataFactory
41+
42+
(selector: Type | string, {descendants, read}?: {
43+
>selector : Type | string
44+
>Type : Type
45+
>descendants : boolean | undefined
46+
>read : any
47+
48+
descendants?: boolean;
49+
>descendants : boolean | undefined
50+
51+
read?: any;
52+
>read : any
53+
54+
}): ParameterDecorator;
55+
>ParameterDecorator : (target: Object, propertyKey: string | symbol, parameterIndex: number) => void
56+
57+
new (selector: Type | string, {descendants, read}?: {
58+
>selector : Type | string
59+
>Type : Type
60+
>descendants : boolean | undefined
61+
>read : any
62+
63+
descendants?: boolean;
64+
>descendants : boolean | undefined
65+
66+
read?: any;
67+
>read : any
68+
69+
}): QueryMetadata;
70+
>QueryMetadata : QueryMetadata
71+
}
72+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strictNullChecks: true
2+
// @declaration: true
3+
4+
declare function f1({ a, b }?: { a: number, b: string }): void;
5+
6+
function f2({ a, b }: { a: number, b: number } = { a: 0, b: 0 }) {
7+
a;
8+
b;
9+
}
10+
11+
// Repro from #8681
12+
13+
interface Type { t: void }
14+
interface QueryMetadata { q: void }
15+
16+
interface QueryMetadataFactory {
17+
(selector: Type | string, {descendants, read}?: {
18+
descendants?: boolean;
19+
read?: any;
20+
}): ParameterDecorator;
21+
new (selector: Type | string, {descendants, read}?: {
22+
descendants?: boolean;
23+
read?: any;
24+
}): QueryMetadata;
25+
}

0 commit comments

Comments
 (0)