Skip to content

Commit

Permalink
Chain RHS narrowing and truthiness narrowing in assignment expression…
Browse files Browse the repository at this point in the history
… narrowing (#31348)
  • Loading branch information
weswigham committed May 13, 2019
1 parent 5460281 commit f140dfc
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Expand Up @@ -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:
Expand Down
@@ -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;
}());
@@ -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))
}
}
}
@@ -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
}
}
}
2 changes: 1 addition & 1 deletion tests/baselines/reference/controlFlowTruthiness.types
Expand Up @@ -111,7 +111,7 @@ function f5() {
>x : string

y; // string | undefined
>y : string | undefined
>y : string
}
else {
x; // string | undefined
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand Up @@ -194,7 +194,7 @@ function foo7(x: number | string | boolean) {
>x : boolean
>x.toString() : string
>x.toString : () => string
>x : boolean
>x : true
>toString : () => string
}

@@ -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;
}
}
}

0 comments on commit f140dfc

Please sign in to comment.