diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f79b80be9fc..e1857d47f7e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8016,10 +8016,13 @@ namespace ts { else if (isUnion) { const indexInfo = !isLateBoundName(name) && (isNumericLiteralName(name) && getIndexInfoOfType(type, IndexKind.Number) || getIndexInfoOfType(type, IndexKind.String)); if (indexInfo) { - checkFlags |= indexInfo.isReadonly ? CheckFlags.Readonly : 0; - checkFlags |= CheckFlags.WritePartial; + checkFlags |= CheckFlags.WritePartial | (indexInfo.isReadonly ? CheckFlags.Readonly : 0); indexTypes = append(indexTypes, isTupleType(type) ? getRestTypeOfTupleType(type) || undefinedType : indexInfo.type); } + else if (isObjectLiteralType(type)) { + checkFlags |= CheckFlags.WritePartial; + indexTypes = append(indexTypes, undefinedType); + } else { checkFlags |= CheckFlags.ReadPartial; } @@ -18451,11 +18454,13 @@ namespace ts { } return contextSensitive === true ? getTypeOfExpression(left) : contextSensitive; case SyntaxKind.BarBarToken: - // When an || expression has a contextual type, the operands are contextually typed by that type. When an || - // expression has no contextual type, the right operand is contextually typed by the type of the left operand, - // except for the special case of Javascript declarations of the form `namespace.prop = namespace.prop || {}` + // When an || expression has a contextual type, the operands are contextually typed by that type, except + // when that type originates in a binding pattern, the right operand is contextually typed by the type of + // the left operand. When an || expression has no contextual type, the right operand is contextually typed + // by the type of the left operand, except for the special case of Javascript declarations of the form + // `namespace.prop = namespace.prop || {}`. const type = getContextualType(binaryExpression, contextFlags); - return !type && node === right && !isDefaultedExpandoInitializer(binaryExpression) ? + return node === right && (type && type.pattern || !type && !isDefaultedExpandoInitializer(binaryExpression)) ? getTypeOfExpression(left) : type; case SyntaxKind.AmpersandAmpersandToken: case SyntaxKind.CommaToken: @@ -20195,7 +20200,8 @@ namespace ts { let propType: Type; const leftType = checkNonNullExpression(left); const parentSymbol = getNodeLinks(left).resolvedSymbol; - const apparentType = getApparentType(getWidenedType(leftType)); + // We widen array literals to get type any[] instead of undefined[] in non-strict mode + const apparentType = getApparentType(isEmptyArrayLiteralType(leftType) ? getWidenedType(leftType) : leftType); if (isTypeAny(apparentType) || apparentType === silentNeverType) { if (isIdentifier(left) && parentSymbol) { markAliasReferenced(parentSymbol, node); diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault.js b/tests/baselines/reference/destructuringAssignmentWithDefault.js index df4addf0269..b2b0c31654b 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault.js +++ b/tests/baselines/reference/destructuringAssignmentWithDefault.js @@ -2,6 +2,34 @@ const a: { x?: number } = { }; let x = 0; ({x = 1} = a); + +// Repro from #26235 + +function f1(options?: { color?: string, width?: number }) { + let { color, width } = options || {}; + ({ color, width } = options || {}); + let x1 = (options || {}).color; + let x2 = (options || {})["color"]; +} + +function f2(options?: [string?, number?]) { + let [str, num] = options || []; + [str, num] = options || []; + let x1 = (options || {})[0]; +} + +function f3(options?: { color: string, width: number }) { + let { color, width } = options || {}; + ({ color, width } = options || {}); + let x1 = (options || {}).color; + let x2 = (options || {})["color"]; +} + +function f4(options?: [string, number]) { + let [str, num] = options || []; + [str, num] = options || []; + let x1 = (options || {})[0]; +} //// [destructuringAssignmentWithDefault.js] @@ -9,3 +37,30 @@ var _a; var a = {}; var x = 0; (_a = a.x, x = _a === void 0 ? 1 : _a); +// Repro from #26235 +function f1(options) { + var _a; + var _b = options || {}, color = _b.color, width = _b.width; + (_a = options || {}, color = _a.color, width = _a.width); + var x1 = (options || {}).color; + var x2 = (options || {})["color"]; +} +function f2(options) { + var _a; + var _b = options || [], str = _b[0], num = _b[1]; + _a = options || [], str = _a[0], num = _a[1]; + var x1 = (options || {})[0]; +} +function f3(options) { + var _a; + var _b = options || {}, color = _b.color, width = _b.width; + (_a = options || {}, color = _a.color, width = _a.width); + var x1 = (options || {}).color; + var x2 = (options || {})["color"]; +} +function f4(options) { + var _a; + var _b = options || [], str = _b[0], num = _b[1]; + _a = options || [], str = _a[0], num = _a[1]; + var x1 = (options || {})[0]; +} diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault.symbols b/tests/baselines/reference/destructuringAssignmentWithDefault.symbols index b011834c4f0..eef4bdcd97a 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault.symbols +++ b/tests/baselines/reference/destructuringAssignmentWithDefault.symbols @@ -10,3 +10,101 @@ let x = 0; >x : Symbol(x, Decl(destructuringAssignmentWithDefault.ts, 2, 2)) >a : Symbol(a, Decl(destructuringAssignmentWithDefault.ts, 0, 5)) +// Repro from #26235 + +function f1(options?: { color?: string, width?: number }) { +>f1 : Symbol(f1, Decl(destructuringAssignmentWithDefault.ts, 2, 14)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12)) +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 6, 39)) + + let { color, width } = options || {}; +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 7, 9)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 7, 16)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12)) + + ({ color, width } = options || {}); +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 8, 6)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 8, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12)) + + let x1 = (options || {}).color; +>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 9, 7)) +>(options || {}).color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12)) +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23)) + + let x2 = (options || {})["color"]; +>x2 : Symbol(x2, Decl(destructuringAssignmentWithDefault.ts, 10, 7)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 6, 12)) +>"color" : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 6, 23)) +} + +function f2(options?: [string?, number?]) { +>f2 : Symbol(f2, Decl(destructuringAssignmentWithDefault.ts, 11, 1)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12)) + + let [str, num] = options || []; +>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 14, 9)) +>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 14, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12)) + + [str, num] = options || []; +>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 14, 9)) +>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 14, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12)) + + let x1 = (options || {})[0]; +>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 16, 7)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 13, 12)) +>0 : Symbol(0) +} + +function f3(options?: { color: string, width: number }) { +>f3 : Symbol(f3, Decl(destructuringAssignmentWithDefault.ts, 17, 1)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12)) +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 19, 38)) + + let { color, width } = options || {}; +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 20, 9)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 20, 16)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12)) + + ({ color, width } = options || {}); +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 21, 6)) +>width : Symbol(width, Decl(destructuringAssignmentWithDefault.ts, 21, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12)) + + let x1 = (options || {}).color; +>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 22, 7)) +>(options || {}).color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12)) +>color : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23)) + + let x2 = (options || {})["color"]; +>x2 : Symbol(x2, Decl(destructuringAssignmentWithDefault.ts, 23, 7)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 19, 12)) +>"color" : Symbol(color, Decl(destructuringAssignmentWithDefault.ts, 19, 23)) +} + +function f4(options?: [string, number]) { +>f4 : Symbol(f4, Decl(destructuringAssignmentWithDefault.ts, 24, 1)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12)) + + let [str, num] = options || []; +>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 27, 9)) +>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 27, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12)) + + [str, num] = options || []; +>str : Symbol(str, Decl(destructuringAssignmentWithDefault.ts, 27, 9)) +>num : Symbol(num, Decl(destructuringAssignmentWithDefault.ts, 27, 13)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12)) + + let x1 = (options || {})[0]; +>x1 : Symbol(x1, Decl(destructuringAssignmentWithDefault.ts, 29, 7)) +>options : Symbol(options, Decl(destructuringAssignmentWithDefault.ts, 26, 12)) +>0 : Symbol(0) +} + diff --git a/tests/baselines/reference/destructuringAssignmentWithDefault.types b/tests/baselines/reference/destructuringAssignmentWithDefault.types index 6347ccd0188..379bfe8b3bd 100644 --- a/tests/baselines/reference/destructuringAssignmentWithDefault.types +++ b/tests/baselines/reference/destructuringAssignmentWithDefault.types @@ -16,3 +16,149 @@ let x = 0; >1 : 1 >a : { x?: number | undefined; } +// Repro from #26235 + +function f1(options?: { color?: string, width?: number }) { +>f1 : (options?: { color?: string | undefined; width?: number | undefined; } | undefined) => void +>options : { color?: string | undefined; width?: number | undefined; } | undefined +>color : string | undefined +>width : number | undefined + + let { color, width } = options || {}; +>color : string | undefined +>width : number | undefined +>options || {} : { color?: string | undefined; width?: number | undefined; } +>options : { color?: string | undefined; width?: number | undefined; } | undefined +>{} : {} + + ({ color, width } = options || {}); +>({ color, width } = options || {}) : { color?: string | undefined; width?: number | undefined; } +>{ color, width } = options || {} : { color?: string | undefined; width?: number | undefined; } +>{ color, width } : { color: string | undefined; width: number | undefined; } +>color : string | undefined +>width : number | undefined +>options || {} : { color?: string | undefined; width?: number | undefined; } +>options : { color?: string | undefined; width?: number | undefined; } | undefined +>{} : {} + + let x1 = (options || {}).color; +>x1 : string | undefined +>(options || {}).color : string | undefined +>(options || {}) : { color?: string | undefined; width?: number | undefined; } +>options || {} : { color?: string | undefined; width?: number | undefined; } +>options : { color?: string | undefined; width?: number | undefined; } | undefined +>{} : {} +>color : string | undefined + + let x2 = (options || {})["color"]; +>x2 : string | undefined +>(options || {})["color"] : string | undefined +>(options || {}) : { color?: string | undefined; width?: number | undefined; } +>options || {} : { color?: string | undefined; width?: number | undefined; } +>options : { color?: string | undefined; width?: number | undefined; } | undefined +>{} : {} +>"color" : "color" +} + +function f2(options?: [string?, number?]) { +>f2 : (options?: [(string | undefined)?, (number | undefined)?] | undefined) => void +>options : [(string | undefined)?, (number | undefined)?] | undefined + + let [str, num] = options || []; +>str : string | undefined +>num : number | undefined +>options || [] : [(string | undefined)?, (number | undefined)?] +>options : [(string | undefined)?, (number | undefined)?] | undefined +>[] : [] + + [str, num] = options || []; +>[str, num] = options || [] : [(string | undefined)?, (number | undefined)?] +>[str, num] : [string | undefined, number | undefined] +>str : string | undefined +>num : number | undefined +>options || [] : [(string | undefined)?, (number | undefined)?] +>options : [(string | undefined)?, (number | undefined)?] | undefined +>[] : [] + + let x1 = (options || {})[0]; +>x1 : string | undefined +>(options || {})[0] : string | undefined +>(options || {}) : [(string | undefined)?, (number | undefined)?] | {} +>options || {} : [(string | undefined)?, (number | undefined)?] | {} +>options : [(string | undefined)?, (number | undefined)?] | undefined +>{} : {} +>0 : 0 +} + +function f3(options?: { color: string, width: number }) { +>f3 : (options?: { color: string; width: number; } | undefined) => void +>options : { color: string; width: number; } | undefined +>color : string +>width : number + + let { color, width } = options || {}; +>color : string | undefined +>width : number | undefined +>options || {} : { color: string; width: number; } | {} +>options : { color: string; width: number; } | undefined +>{} : {} + + ({ color, width } = options || {}); +>({ color, width } = options || {}) : { color: string; width: number; } | {} +>{ color, width } = options || {} : { color: string; width: number; } | {} +>{ color, width } : { color: string | undefined; width: number | undefined; } +>color : string | undefined +>width : number | undefined +>options || {} : { color: string; width: number; } | {} +>options : { color: string; width: number; } | undefined +>{} : {} + + let x1 = (options || {}).color; +>x1 : string | undefined +>(options || {}).color : string | undefined +>(options || {}) : { color: string; width: number; } | {} +>options || {} : { color: string; width: number; } | {} +>options : { color: string; width: number; } | undefined +>{} : {} +>color : string | undefined + + let x2 = (options || {})["color"]; +>x2 : string | undefined +>(options || {})["color"] : string | undefined +>(options || {}) : { color: string; width: number; } | {} +>options || {} : { color: string; width: number; } | {} +>options : { color: string; width: number; } | undefined +>{} : {} +>"color" : "color" +} + +function f4(options?: [string, number]) { +>f4 : (options?: [string, number] | undefined) => void +>options : [string, number] | undefined + + let [str, num] = options || []; +>str : string | undefined +>num : number | undefined +>options || [] : [] | [string, number] +>options : [string, number] | undefined +>[] : [] + + [str, num] = options || []; +>[str, num] = options || [] : [] | [string, number] +>[str, num] : [string | undefined, number | undefined] +>str : string | undefined +>num : number | undefined +>options || [] : [] | [string, number] +>options : [string, number] | undefined +>[] : [] + + let x1 = (options || {})[0]; +>x1 : string | undefined +>(options || {})[0] : string | undefined +>(options || {}) : [string, number] | {} +>options || {} : [string, number] | {} +>options : [string, number] | undefined +>{} : {} +>0 : 0 +} + diff --git a/tests/baselines/reference/initializedDestructuringAssignmentTypes.types b/tests/baselines/reference/initializedDestructuringAssignmentTypes.types index bb30a395cff..333fc5d92cc 100644 --- a/tests/baselines/reference/initializedDestructuringAssignmentTypes.types +++ b/tests/baselines/reference/initializedDestructuringAssignmentTypes.types @@ -9,7 +9,7 @@ const [, a = ''] = ''.match('') || []; >'' : "" >match : (regexp: string | RegExp) => RegExpMatchArray >'' : "" ->[] : [undefined?, ""?] +>[] : undefined[] a.toFixed() >a.toFixed() : any diff --git a/tests/cases/compiler/destructuringAssignmentWithDefault.ts b/tests/cases/compiler/destructuringAssignmentWithDefault.ts index 45ade402eb8..76b0e71c373 100644 --- a/tests/cases/compiler/destructuringAssignmentWithDefault.ts +++ b/tests/cases/compiler/destructuringAssignmentWithDefault.ts @@ -2,3 +2,31 @@ const a: { x?: number } = { }; let x = 0; ({x = 1} = a); + +// Repro from #26235 + +function f1(options?: { color?: string, width?: number }) { + let { color, width } = options || {}; + ({ color, width } = options || {}); + let x1 = (options || {}).color; + let x2 = (options || {})["color"]; +} + +function f2(options?: [string?, number?]) { + let [str, num] = options || []; + [str, num] = options || []; + let x1 = (options || {})[0]; +} + +function f3(options?: { color: string, width: number }) { + let { color, width } = options || {}; + ({ color, width } = options || {}); + let x1 = (options || {}).color; + let x2 = (options || {})["color"]; +} + +function f4(options?: [string, number]) { + let [str, num] = options || []; + [str, num] = options || []; + let x1 = (options || {})[0]; +}