From f140dfc30b9395a4aa5ee82e752d5012e4b9c7de Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 13 May 2019 14:41:33 -0700 Subject: [PATCH] Chain RHS narrowing and truthiness narrowing in assignment expression narrowing (#31348) --- src/compiler/checker.ts | 2 +- ...olFlowForCompoundAssignmentToThisMember.js | 36 ++++++++++ ...wForCompoundAssignmentToThisMember.symbols | 57 ++++++++++++++++ ...lowForCompoundAssignmentToThisMember.types | 67 +++++++++++++++++++ .../reference/controlFlowTruthiness.types | 2 +- .../typeGuardsInConditionalExpression.types | 10 +-- ...GuardsInRightOperandOfAndAndOperator.types | 14 ++-- ...peGuardsInRightOperandOfOrOrOperator.types | 2 +- ...olFlowForCompoundAssignmentToThisMember.ts | 19 ++++++ 9 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.js create mode 100644 tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.symbols create mode 100644 tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.types create mode 100644 tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 64834befee1c8..e07ae98938344 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16773,7 +16773,7 @@ namespace ts { function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: - return narrowTypeByTruthiness(type, expr.left, assumeTrue); + return narrowTypeByTruthiness(narrowType(type, expr.right, assumeTrue), expr.left, assumeTrue); case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: diff --git a/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.js b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.js new file mode 100644 index 0000000000000..cdf74a58996f5 --- /dev/null +++ b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.js @@ -0,0 +1,36 @@ +//// [controlFlowForCompoundAssignmentToThisMember.ts] +class DatasourceCommandWidgetElement { + _commandBased: boolean; + _commandElement: unknown; + commandElement: unknown; + + constructor(target: unknown) { + if (target instanceof DatasourceCommandWidgetElement) { + this._commandBased = true; + this._commandElement = target.commandElement; + } else { + this._commandBased = false; + } + + if (this._commandBased = (target instanceof DatasourceCommandWidgetElement)) { + this._commandElement = target.commandElement; + } + } +} + +//// [controlFlowForCompoundAssignmentToThisMember.js] +var DatasourceCommandWidgetElement = /** @class */ (function () { + function DatasourceCommandWidgetElement(target) { + if (target instanceof DatasourceCommandWidgetElement) { + this._commandBased = true; + this._commandElement = target.commandElement; + } + else { + this._commandBased = false; + } + if (this._commandBased = (target instanceof DatasourceCommandWidgetElement)) { + this._commandElement = target.commandElement; + } + } + return DatasourceCommandWidgetElement; +}()); diff --git a/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.symbols b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.symbols new file mode 100644 index 0000000000000..4da7454f312ad --- /dev/null +++ b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.symbols @@ -0,0 +1,57 @@ +=== tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts === +class DatasourceCommandWidgetElement { +>DatasourceCommandWidgetElement : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) + + _commandBased: boolean; +>_commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) + + _commandElement: unknown; +>_commandElement : Symbol(DatasourceCommandWidgetElement._commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 1, 27)) + + commandElement: unknown; +>commandElement : Symbol(DatasourceCommandWidgetElement.commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 2, 29)) + + constructor(target: unknown) { +>target : Symbol(target, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 5, 16)) + + if (target instanceof DatasourceCommandWidgetElement) { +>target : Symbol(target, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 5, 16)) +>DatasourceCommandWidgetElement : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) + + this._commandBased = true; +>this._commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) +>this : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) +>_commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) + + this._commandElement = target.commandElement; +>this._commandElement : Symbol(DatasourceCommandWidgetElement._commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 1, 27)) +>this : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) +>_commandElement : Symbol(DatasourceCommandWidgetElement._commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 1, 27)) +>target.commandElement : Symbol(DatasourceCommandWidgetElement.commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 2, 29)) +>target : Symbol(target, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 5, 16)) +>commandElement : Symbol(DatasourceCommandWidgetElement.commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 2, 29)) + + } else { + this._commandBased = false; +>this._commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) +>this : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) +>_commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) + } + + if (this._commandBased = (target instanceof DatasourceCommandWidgetElement)) { +>this._commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) +>this : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) +>_commandBased : Symbol(DatasourceCommandWidgetElement._commandBased, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 38)) +>target : Symbol(target, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 5, 16)) +>DatasourceCommandWidgetElement : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) + + this._commandElement = target.commandElement; +>this._commandElement : Symbol(DatasourceCommandWidgetElement._commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 1, 27)) +>this : Symbol(DatasourceCommandWidgetElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 0, 0)) +>_commandElement : Symbol(DatasourceCommandWidgetElement._commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 1, 27)) +>target.commandElement : Symbol(DatasourceCommandWidgetElement.commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 2, 29)) +>target : Symbol(target, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 5, 16)) +>commandElement : Symbol(DatasourceCommandWidgetElement.commandElement, Decl(controlFlowForCompoundAssignmentToThisMember.ts, 2, 29)) + } + } +} diff --git a/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.types b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.types new file mode 100644 index 0000000000000..7c289daa2e017 --- /dev/null +++ b/tests/baselines/reference/controlFlowForCompoundAssignmentToThisMember.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts === +class DatasourceCommandWidgetElement { +>DatasourceCommandWidgetElement : DatasourceCommandWidgetElement + + _commandBased: boolean; +>_commandBased : boolean + + _commandElement: unknown; +>_commandElement : unknown + + commandElement: unknown; +>commandElement : unknown + + constructor(target: unknown) { +>target : unknown + + if (target instanceof DatasourceCommandWidgetElement) { +>target instanceof DatasourceCommandWidgetElement : boolean +>target : unknown +>DatasourceCommandWidgetElement : typeof DatasourceCommandWidgetElement + + this._commandBased = true; +>this._commandBased = true : true +>this._commandBased : boolean +>this : this +>_commandBased : boolean +>true : true + + this._commandElement = target.commandElement; +>this._commandElement = target.commandElement : unknown +>this._commandElement : unknown +>this : this +>_commandElement : unknown +>target.commandElement : unknown +>target : DatasourceCommandWidgetElement +>commandElement : unknown + + } else { + this._commandBased = false; +>this._commandBased = false : false +>this._commandBased : boolean +>this : this +>_commandBased : boolean +>false : false + } + + if (this._commandBased = (target instanceof DatasourceCommandWidgetElement)) { +>this._commandBased = (target instanceof DatasourceCommandWidgetElement) : boolean +>this._commandBased : boolean +>this : this +>_commandBased : boolean +>(target instanceof DatasourceCommandWidgetElement) : boolean +>target instanceof DatasourceCommandWidgetElement : boolean +>target : unknown +>DatasourceCommandWidgetElement : typeof DatasourceCommandWidgetElement + + this._commandElement = target.commandElement; +>this._commandElement = target.commandElement : unknown +>this._commandElement : unknown +>this : this +>_commandElement : unknown +>target.commandElement : unknown +>target : DatasourceCommandWidgetElement +>commandElement : unknown + } + } +} diff --git a/tests/baselines/reference/controlFlowTruthiness.types b/tests/baselines/reference/controlFlowTruthiness.types index 454d094f40651..17f497267ee6e 100644 --- a/tests/baselines/reference/controlFlowTruthiness.types +++ b/tests/baselines/reference/controlFlowTruthiness.types @@ -111,7 +111,7 @@ function f5() { >x : string y; // string | undefined ->y : string | undefined +>y : string } else { x; // string | undefined diff --git a/tests/baselines/reference/typeGuardsInConditionalExpression.types b/tests/baselines/reference/typeGuardsInConditionalExpression.types index 19e11e2e888af..f3b0a045e6b49 100644 --- a/tests/baselines/reference/typeGuardsInConditionalExpression.types +++ b/tests/baselines/reference/typeGuardsInConditionalExpression.types @@ -211,11 +211,11 @@ function foo8(x: number | string | boolean) { >typeof x === "boolean" ? x // boolean : x == 10 : boolean >typeof x === "boolean" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"boolean" : "boolean" ? x // boolean ->x : boolean +>x : true : x == 10)); // boolean >x == 10 : boolean @@ -286,7 +286,7 @@ function foo10(x: number | string | boolean) { && typeof x === "number" >typeof x === "number" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"number" : "number" && x.toString()); // x is number @@ -326,7 +326,7 @@ function foo11(x: number | string | boolean) { && typeof x === "number" >typeof x === "number" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"number" : "number" && (x = 10) // assignment to x @@ -379,7 +379,7 @@ function foo12(x: number | string | boolean) { && typeof x === "number" >typeof x === "number" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"number" : "number" && x); // x is number diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types index 166dd9ac0e1df..43766b1db12a4 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types +++ b/tests/baselines/reference/typeGuardsInRightOperandOfAndAndOperator.types @@ -102,11 +102,11 @@ function foo5(x: number | string | boolean) { >typeof x !== "number" // number | boolean && x : boolean >typeof x !== "number" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"number" : "number" && x)); // boolean ->x : boolean +>x : true } function foo6(x: number | string | boolean) { >foo6 : (x: string | number | boolean) => boolean @@ -167,7 +167,7 @@ function foo7(x: number | string | boolean) { >typeof x === "number" // change value of x ? ((x = 10) && x.toString()) // x is number // do not change value : ((y = x) && x.toString()) : string >typeof x === "number" : boolean >typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" ->x : number | boolean +>x : number | true >"number" : "number" // change value of x @@ -187,13 +187,13 @@ function foo7(x: number | string | boolean) { : ((y = x) && x.toString()))); // x is boolean >((y = x) && x.toString()) : string >(y = x) && x.toString() : string ->(y = x) : boolean ->y = x : boolean +>(y = x) : true +>y = x : true >y : string | number | boolean ->x : boolean +>x : true >x.toString() : string >x.toString : () => string ->x : boolean +>x : true >toString : () => string } diff --git a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types index d3335cd75becf..8f381e115f5c3 100644 --- a/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types +++ b/tests/baselines/reference/typeGuardsInRightOperandOfOrOrOperator.types @@ -194,7 +194,7 @@ function foo7(x: number | string | boolean) { >x : boolean >x.toString() : string >x.toString : () => string ->x : boolean +>x : true >toString : () => string } diff --git a/tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts b/tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts new file mode 100644 index 0000000000000..2ed77e8ed5f29 --- /dev/null +++ b/tests/cases/compiler/controlFlowForCompoundAssignmentToThisMember.ts @@ -0,0 +1,19 @@ + +class DatasourceCommandWidgetElement { + _commandBased: boolean; + _commandElement: unknown; + commandElement: unknown; + + constructor(target: unknown) { + if (target instanceof DatasourceCommandWidgetElement) { + this._commandBased = true; + this._commandElement = target.commandElement; + } else { + this._commandBased = false; + } + + if (this._commandBased = (target instanceof DatasourceCommandWidgetElement)) { + this._commandElement = target.commandElement; + } + } +} \ No newline at end of file