Skip to content

Fix destructuring with fallback #31711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 2, 2019
Merged
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
20 changes: 13 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -18435,11 +18438,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:
Expand Down Expand Up @@ -20179,7 +20184,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);
Expand Down
55 changes: 55 additions & 0 deletions tests/baselines/reference/destructuringAssignmentWithDefault.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,65 @@
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]
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];
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

146 changes: 146 additions & 0 deletions tests/baselines/reference/destructuringAssignmentWithDefault.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const [, a = ''] = ''.match('') || [];
>'' : ""
>match : (regexp: string | RegExp) => RegExpMatchArray
>'' : ""
>[] : [undefined?, ""?]
>[] : undefined[]

a.toFixed()
>a.toFixed() : any
Expand Down
Loading