Skip to content

Commit d6c323a

Browse files
authored
Merge pull request #31711 from microsoft/fixDestructuringWithFallback
Fix destructuring with fallback
2 parents eb81fea + 48d343b commit d6c323a

File tree

6 files changed

+341
-8
lines changed

6 files changed

+341
-8
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8016,10 +8016,13 @@ namespace ts {
80168016
else if (isUnion) {
80178017
const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String));
80188018
if (indexInfo) {
8019-
checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0;
8020-
checkFlags |= CheckFlags.WritePartial;
8019+
checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0);
80218020
indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type);
80228021
}
8022+
else if (isObjectLiteralType(type)) {
8023+
checkFlags |= CheckFlags.WritePartial;
8024+
indexTypes = append(indexTypes, undefinedType);
8025+
}
80238026
else {
80248027
checkFlags |= CheckFlags.ReadPartial;
80258028
}
@@ -18451,11 +18454,13 @@ namespace ts {
1845118454
}
1845218455
return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive;
1845318456
case SyntaxKind.BarBarToken:
18454-
// When an || expression has a contextual type, the operands are contextually typed by that type. When an ||
18455-
// expression has no contextual type, the right operand is contextually typed by the type of the left operand,
18456-
// except for the special case of Javascript declarations of the form `namespace.prop = namespace.prop || {}`
18457+
// When an || expression has a contextual type, the operands are contextually typed by that type, except
18458+
// when that type originates in a binding pattern, the right operand is contextually typed by the type of
18459+
// the left operand. When an || expression has no contextual type, the right operand is contextually typed
18460+
// by the type of the left operand, except for the special case of Javascript declarations of the form
18461+
// `namespace.prop = namespace.prop || {}`.
1845718462
const type = getContextualType(binaryExpression, contextFlags);
18458-
return !type && node === right && !isDefaultedExpandoInitializer(binaryExpression) ?
18463+
return node === right && (type && type.pattern || !type && !isDefaultedExpandoInitializer(binaryExpression)) ?
1845918464
getTypeOfExpression(left) : type;
1846018465
case SyntaxKind.AmpersandAmpersandToken:
1846118466
case SyntaxKind.CommaToken:
@@ -20195,7 +20200,8 @@ namespace ts {
2019520200
let propType: Type;
2019620201
const leftType = checkNonNullExpression(left);
2019720202
const parentSymbol = getNodeLinks(left).resolvedSymbol;
20198-
const apparentType = getApparentType(getWidenedType(leftType));
20203+
// We widen array literals to get type any[] instead of undefined[] in non-strict mode
20204+
const apparentType = getApparentType(isEmptyArrayLiteralType(leftType) ? getWidenedType(leftType) : leftType);
2019920205
if (isTypeAny(apparentType) || apparentType === silentNeverType) {
2020020206
if (isIdentifier(left) && parentSymbol) {
2020120207
markAliasReferenced(parentSymbol, node);

tests/baselines/reference/destructuringAssignmentWithDefault.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,65 @@
22
const a: { x?: number } = { };
33
let x = 0;
44
({x = 1} = a);
5+
6+
// Repro from #26235
7+
8+
function f1(options?: { color?: string, width?: number }) {
9+
let { color, width } = options || {};
10+
({ color, width } = options || {});
11+
let x1 = (options || {}).color;
12+
let x2 = (options || {})["color"];
13+
}
14+
15+
function f2(options?: [string?, number?]) {
16+
let [str, num] = options || [];
17+
[str, num] = options || [];
18+
let x1 = (options || {})[0];
19+
}
20+
21+
function f3(options?: { color: string, width: number }) {
22+
let { color, width } = options || {};
23+
({ color, width } = options || {});
24+
let x1 = (options || {}).color;
25+
let x2 = (options || {})["color"];
26+
}
27+
28+
function f4(options?: [string, number]) {
29+
let [str, num] = options || [];
30+
[str, num] = options || [];
31+
let x1 = (options || {})[0];
32+
}
533

634

735
//// [destructuringAssignmentWithDefault.js]
836
var _a;
937
var a = {};
1038
var x = 0;
1139
(_a = a.x, x = _a === void 0 ? 1 : _a);
40+
// Repro from #26235
41+
function f1(options) {
42+
var _a;
43+
var _b = options || {}, color = _b.color, width = _b.width;
44+
(_a = options || {}, color = _a.color, width = _a.width);
45+
var x1 = (options || {}).color;
46+
var x2 = (options || {})["color"];
47+
}
48+
function f2(options) {
49+
var _a;
50+
var _b = options || [], str = _b[0], num = _b[1];
51+
_a = options || [], str = _a[0], num = _a[1];
52+
var x1 = (options || {})[0];
53+
}
54+
function f3(options) {
55+
var _a;
56+
var _b = options || {}, color = _b.color, width = _b.width;
57+
(_a = options || {}, color = _a.color, width = _a.width);
58+
var x1 = (options || {}).color;
59+
var x2 = (options || {})["color"];
60+
}
61+
function f4(options) {
62+
var _a;
63+
var _b = options || [], str = _b[0], num = _b[1];
64+
_a = options || [], str = _a[0], num = _a[1];
65+
var x1 = (options || {})[0];
66+
}

tests/baselines/reference/destructuringAssignmentWithDefault.symbols

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,101 @@ let x = 0;
1010
>x : Symbol(x, Decl(destructuringAssignmentWithDefault.ts, 2, 2))
1111
>a : Symbol(a, Decl(destructuringAssignmentWithDefault.ts, 0, 5))
1212

13+
// Repro from #26235
14+
15+
function f1(options?: { color?: string, width?: number }) {
16+
>f1 : Symbol(f1, Decl(destructuringAssignmentWithDefault.ts, 2, 14))
17+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12))
18+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23))
19+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 6, 39))
20+
21+
let { color, width } = options || {};
22+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 7, 9))
23+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 7, 16))
24+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12))
25+
26+
({ color, width } = options || {});
27+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 8, 6))
28+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 8, 13))
29+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12))
30+
31+
let x1 = (options || {}).color;
32+
>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 9, 7))
33+
>(options || {}).color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23))
34+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12))
35+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23))
36+
37+
let x2 = (options || {})["color"];
38+
>x2 : Symbol(x2, Decl(destructuringAssignmentWithDefault.ts, 10, 7))
39+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12))
40+
>"color" : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23))
41+
}
42+
43+
function f2(options?: [string?, number?]) {
44+
>f2 : Symbol(f2, Decl(destructuringAssignmentWithDefault.ts, 11, 1))
45+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12))
46+
47+
let [str, num] = options || [];
48+
>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 14, 9))
49+
>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 14, 13))
50+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12))
51+
52+
[str, num] = options || [];
53+
>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 14, 9))
54+
>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 14, 13))
55+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12))
56+
57+
let x1 = (options || {})[0];
58+
>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 16, 7))
59+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12))
60+
>0 : Symbol(0)
61+
}
62+
63+
function f3(options?: { color: string, width: number }) {
64+
>f3 : Symbol(f3, Decl(destructuringAssignmentWithDefault.ts, 17, 1))
65+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12))
66+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23))
67+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 19, 38))
68+
69+
let { color, width } = options || {};
70+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 20, 9))
71+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 20, 16))
72+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12))
73+
74+
({ color, width } = options || {});
75+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 21, 6))
76+
>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 21, 13))
77+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12))
78+
79+
let x1 = (options || {}).color;
80+
>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 22, 7))
81+
>(options || {}).color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23))
82+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12))
83+
>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23))
84+
85+
let x2 = (options || {})["color"];
86+
>x2 : Symbol(x2, Decl(destructuringAssignmentWithDefault.ts, 23, 7))
87+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12))
88+
>"color" : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23))
89+
}
90+
91+
function f4(options?: [string, number]) {
92+
>f4 : Symbol(f4, Decl(destructuringAssignmentWithDefault.ts, 24, 1))
93+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12))
94+
95+
let [str, num] = options || [];
96+
>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 27, 9))
97+
>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 27, 13))
98+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12))
99+
100+
[str, num] = options || [];
101+
>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 27, 9))
102+
>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 27, 13))
103+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12))
104+
105+
let x1 = (options || {})[0];
106+
>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 29, 7))
107+
>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12))
108+
>0 : Symbol(0)
109+
}
110+

tests/baselines/reference/destructuringAssignmentWithDefault.types

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,149 @@ let x = 0;
1616
>1 : 1
1717
>a : { x?: number | undefined; }
1818

19+
// Repro from #26235
20+
21+
function f1(options?: { color?: string, width?: number }) {
22+
>f1 : (options?: { color?: string | undefined; width?: number | undefined; } | undefined) => void
23+
>options : { color?: string | undefined; width?: number | undefined; } | undefined
24+
>color : string | undefined
25+
>width : number | undefined
26+
27+
let { color, width } = options || {};
28+
>color : string | undefined
29+
>width : number | undefined
30+
>options || {} : { color?: string | undefined; width?: number | undefined; }
31+
>options : { color?: string | undefined; width?: number | undefined; } | undefined
32+
>{} : {}
33+
34+
({ color, width } = options || {});
35+
>({ color, width } = options || {}) : { color?: string | undefined; width?: number | undefined; }
36+
>{ color, width } = options || {} : { color?: string | undefined; width?: number | undefined; }
37+
>{ color, width } : { color: string | undefined; width: number | undefined; }
38+
>color : string | undefined
39+
>width : number | undefined
40+
>options || {} : { color?: string | undefined; width?: number | undefined; }
41+
>options : { color?: string | undefined; width?: number | undefined; } | undefined
42+
>{} : {}
43+
44+
let x1 = (options || {}).color;
45+
>x1 : string | undefined
46+
>(options || {}).color : string | undefined
47+
>(options || {}) : { color?: string | undefined; width?: number | undefined; }
48+
>options || {} : { color?: string | undefined; width?: number | undefined; }
49+
>options : { color?: string | undefined; width?: number | undefined; } | undefined
50+
>{} : {}
51+
>color : string | undefined
52+
53+
let x2 = (options || {})["color"];
54+
>x2 : string | undefined
55+
>(options || {})["color"] : string | undefined
56+
>(options || {}) : { color?: string | undefined; width?: number | undefined; }
57+
>options || {} : { color?: string | undefined; width?: number | undefined; }
58+
>options : { color?: string | undefined; width?: number | undefined; } | undefined
59+
>{} : {}
60+
>"color" : "color"
61+
}
62+
63+
function f2(options?: [string?, number?]) {
64+
>f2 : (options?: [(string | undefined)?, (number | undefined)?] | undefined) => void
65+
>options : [(string | undefined)?, (number | undefined)?] | undefined
66+
67+
let [str, num] = options || [];
68+
>str : string | undefined
69+
>num : number | undefined
70+
>options || [] : [(string | undefined)?, (number | undefined)?]
71+
>options : [(string | undefined)?, (number | undefined)?] | undefined
72+
>[] : []
73+
74+
[str, num] = options || [];
75+
>[str, num] = options || [] : [(string | undefined)?, (number | undefined)?]
76+
>[str, num] : [string | undefined, number | undefined]
77+
>str : string | undefined
78+
>num : number | undefined
79+
>options || [] : [(string | undefined)?, (number | undefined)?]
80+
>options : [(string | undefined)?, (number | undefined)?] | undefined
81+
>[] : []
82+
83+
let x1 = (options || {})[0];
84+
>x1 : string | undefined
85+
>(options || {})[0] : string | undefined
86+
>(options || {}) : [(string | undefined)?, (number | undefined)?] | {}
87+
>options || {} : [(string | undefined)?, (number | undefined)?] | {}
88+
>options : [(string | undefined)?, (number | undefined)?] | undefined
89+
>{} : {}
90+
>0 : 0
91+
}
92+
93+
function f3(options?: { color: string, width: number }) {
94+
>f3 : (options?: { color: string; width: number; } | undefined) => void
95+
>options : { color: string; width: number; } | undefined
96+
>color : string
97+
>width : number
98+
99+
let { color, width } = options || {};
100+
>color : string | undefined
101+
>width : number | undefined
102+
>options || {} : { color: string; width: number; } | {}
103+
>options : { color: string; width: number; } | undefined
104+
>{} : {}
105+
106+
({ color, width } = options || {});
107+
>({ color, width } = options || {}) : { color: string; width: number; } | {}
108+
>{ color, width } = options || {} : { color: string; width: number; } | {}
109+
>{ color, width } : { color: string | undefined; width: number | undefined; }
110+
>color : string | undefined
111+
>width : number | undefined
112+
>options || {} : { color: string; width: number; } | {}
113+
>options : { color: string; width: number; } | undefined
114+
>{} : {}
115+
116+
let x1 = (options || {}).color;
117+
>x1 : string | undefined
118+
>(options || {}).color : string | undefined
119+
>(options || {}) : { color: string; width: number; } | {}
120+
>options || {} : { color: string; width: number; } | {}
121+
>options : { color: string; width: number; } | undefined
122+
>{} : {}
123+
>color : string | undefined
124+
125+
let x2 = (options || {})["color"];
126+
>x2 : string | undefined
127+
>(options || {})["color"] : string | undefined
128+
>(options || {}) : { color: string; width: number; } | {}
129+
>options || {} : { color: string; width: number; } | {}
130+
>options : { color: string; width: number; } | undefined
131+
>{} : {}
132+
>"color" : "color"
133+
}
134+
135+
function f4(options?: [string, number]) {
136+
>f4 : (options?: [string, number] | undefined) => void
137+
>options : [string, number] | undefined
138+
139+
let [str, num] = options || [];
140+
>str : string | undefined
141+
>num : number | undefined
142+
>options || [] : [] | [string, number]
143+
>options : [string, number] | undefined
144+
>[] : []
145+
146+
[str, num] = options || [];
147+
>[str, num] = options || [] : [] | [string, number]
148+
>[str, num] : [string | undefined, number | undefined]
149+
>str : string | undefined
150+
>num : number | undefined
151+
>options || [] : [] | [string, number]
152+
>options : [string, number] | undefined
153+
>[] : []
154+
155+
let x1 = (options || {})[0];
156+
>x1 : string | undefined
157+
>(options || {})[0] : string | undefined
158+
>(options || {}) : [string, number] | {}
159+
>options || {} : [string, number] | {}
160+
>options : [string, number] | undefined
161+
>{} : {}
162+
>0 : 0
163+
}
164+

tests/baselines/reference/initializedDestructuringAssignmentTypes.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const [, a = ''] = ''.match('') || [];
99
>'' : ""
1010
>match : (regexp: string | RegExp) => RegExpMatchArray
1111
>'' : ""
12-
>[] : [undefined?, ""?]
12+
>[] : undefined[]
1313

1414
a.toFixed()
1515
>a.toFixed() : any

0 commit comments

Comments
 (0)