Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0e30aa1

Browse files
committedApr 26, 2016
Reconsile with CFA work
1 parent 74f3bd0 commit 0e30aa1

File tree

3 files changed

+116
-19
lines changed

3 files changed

+116
-19
lines changed
 

‎src/compiler/binder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -623,13 +623,13 @@ namespace ts {
623623
case SyntaxKind.ExclamationEqualsToken:
624624
case SyntaxKind.EqualsEqualsEqualsToken:
625625
case SyntaxKind.ExclamationEqualsEqualsToken:
626-
if (isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) {
626+
if (isNarrowingExpression(expr.left)) {
627627
return true;
628628
}
629629
if (expr.left.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>expr.left).expression) && expr.right.kind === SyntaxKind.StringLiteral) {
630630
return true;
631631
}
632-
return false;
632+
return true;
633633
case SyntaxKind.InstanceOfKeyword:
634634
return isNarrowingExpression(expr.left);
635635
case SyntaxKind.CommaToken:

‎src/compiler/checker.ts

+98-1
Original file line numberDiff line numberDiff line change
@@ -7698,6 +7698,103 @@ namespace ts {
76987698
return isMatchingReference(expr, reference) ? getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy) : type;
76997699
}
77007700

7701+
function narrowTypeByValueExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
7702+
assumeTrue = (expr.operatorToken.kind === SyntaxKind.EqualsEqualsEqualsToken || expr.operatorToken.kind === SyntaxKind.EqualsEqualsToken) ? assumeTrue : !assumeTrue;
7703+
let lhs = expr.left;
7704+
// selectors is the stack of property names used to select down into a type to get the member being narrowed
7705+
const selectors: string[] = [];
7706+
while (lhs.kind !== SyntaxKind.Identifier) {
7707+
switch (lhs.kind) {
7708+
case SyntaxKind.ParenthesizedExpression:
7709+
lhs = (lhs as ParenthesizedExpression).expression;
7710+
break;
7711+
case SyntaxKind.PropertyAccessExpression:
7712+
const name = (lhs as PropertyAccessExpression).name.text;
7713+
// If a name doesn't resolve, bail
7714+
if (name === undefined) {
7715+
return type;
7716+
}
7717+
selectors.push(name);
7718+
lhs = (lhs as PropertyAccessExpression).expression;
7719+
break;
7720+
case SyntaxKind.Identifier:
7721+
break;
7722+
default:
7723+
// Unhandled control flow construct, don't narrow
7724+
return type;
7725+
}
7726+
}
7727+
7728+
if (!isMatchingReference(lhs, reference)) {
7729+
return type;
7730+
}
7731+
const rhsType = checkExpressionCached(expr.right);
7732+
7733+
if (assumeTrue) {
7734+
return narrowIntrospectively(type);
7735+
}
7736+
return type;
7737+
7738+
/**
7739+
* Descend into the type using the selectors we accumulated above and narrow any unions along the way
7740+
* If assumeTrue, we narrow by removing all types not compatible with the rhs type
7741+
* If not, we narrow only if the rhsType is a Value type (ie, StringLiteral) by removing all types compatible with that type (TODO)
7742+
*/
7743+
function narrowIntrospectively(type: Type) {
7744+
const propName = selectors.pop();
7745+
if (propName === undefined) {
7746+
// Selected all the way into the object, return the type for the property to be narrowed
7747+
if (isTypeSubtypeOf(rhsType, type)) {
7748+
return rhsType;
7749+
}
7750+
else {
7751+
return type;
7752+
}
7753+
}
7754+
if (type.flags & TypeFlags.Union) {
7755+
const reducedUnion = getUnionType(
7756+
filter((type as UnionType).types, t => isMemberSubtype(t, rhsType, [...selectors, propName])),
7757+
/*noSubtypeReduction*/ true
7758+
);
7759+
7760+
if (reducedUnion !== emptyUnionType) {
7761+
return narrowBasedOnMatchingProperty(reducedUnion, propName);
7762+
}
7763+
else {
7764+
return type;
7765+
}
7766+
}
7767+
7768+
return narrowBasedOnMatchingProperty(type, propName);
7769+
}
7770+
7771+
function isMemberSubtype(type: Type, check: Type, selectors: string[]): boolean {
7772+
if (!selectors.length) {
7773+
return isTypeSubtypeOf(type, check);
7774+
}
7775+
const name = selectors.pop();
7776+
const childProp = getPropertyOfType(type, name);
7777+
const propType = childProp && getTypeOfSymbol(childProp);
7778+
return propType && isMemberSubtype(propType, check, selectors);
7779+
}
7780+
7781+
function narrowBasedOnMatchingProperty(type: Type, name: string): Type {
7782+
const childProp = getPropertyOfType(type, name);
7783+
const propType = childProp && getTypeOfSymbol(childProp);
7784+
const narrowedType = propType && narrowIntrospectively(propType);
7785+
7786+
if (narrowedType && !isTypeIdenticalTo(propType, narrowedType)) {
7787+
const symbols = cloneSymbolTable(resolveStructuredTypeMembers(type as ObjectType).members);
7788+
const temp = createSymbol(childProp.flags, name);
7789+
getSymbolLinks(temp).type = narrowedType;
7790+
symbols[name] = temp;
7791+
return createAnonymousType(createSymbol(type.symbol.flags, type.symbol.name), symbols, getSignaturesOfType(type, SignatureKind.Call),
7792+
getSignaturesOfType(type, SignatureKind.Construct), getIndexInfoOfType(type, IndexKind.String), getIndexInfoOfType(type, IndexKind.Number));
7793+
}
7794+
return type;
7795+
}
7796+
}
7797+
77017798
function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
77027799
switch (expr.operatorToken.kind) {
77037800
case SyntaxKind.EqualsToken:
@@ -7712,7 +7809,7 @@ namespace ts {
77127809
if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral) {
77137810
return narrowTypeByTypeof(type, expr, assumeTrue);
77147811
}
7715-
break;
7812+
return narrowTypeByValueExpression(type, expr, assumeTrue);
77167813
case SyntaxKind.InstanceOfKeyword:
77177814
return narrowTypeByInstanceof(type, expr, assumeTrue);
77187815
case SyntaxKind.CommaToken:

‎tests/baselines/reference/typeGuardByEqualityCheck.symbols

+16-16
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,30 @@ interface Discriminator {
33
>Discriminator : Symbol(Discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 0))
44

55
_discriminator: void;
6-
>_discriminator : Symbol(_discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 25))
6+
>_discriminator : Symbol(Discriminator._discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 25))
77
}
88

99
interface FooDiscriminator extends Discriminator {
1010
>FooDiscriminator : Symbol(FooDiscriminator, Decl(typeGuardByEqualityCheck.ts, 2, 1))
1111
>Discriminator : Symbol(Discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 0))
1212

1313
_foo: void;
14-
>_foo : Symbol(_foo, Decl(typeGuardByEqualityCheck.ts, 4, 50))
14+
>_foo : Symbol(FooDiscriminator._foo, Decl(typeGuardByEqualityCheck.ts, 4, 50))
1515
}
1616

1717
interface BarDiscriminator extends Discriminator {
1818
>BarDiscriminator : Symbol(BarDiscriminator, Decl(typeGuardByEqualityCheck.ts, 6, 1))
1919
>Discriminator : Symbol(Discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 0))
2020

2121
_bar: void;
22-
>_bar : Symbol(_bar, Decl(typeGuardByEqualityCheck.ts, 8, 50))
22+
>_bar : Symbol(BarDiscriminator._bar, Decl(typeGuardByEqualityCheck.ts, 8, 50))
2323
}
2424

2525
interface BaseNode {
2626
>BaseNode : Symbol(BaseNode, Decl(typeGuardByEqualityCheck.ts, 10, 1))
2727

2828
kind: Discriminator;
29-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 12, 20))
29+
>kind : Symbol(BaseNode.kind, Decl(typeGuardByEqualityCheck.ts, 12, 20))
3030
>Discriminator : Symbol(Discriminator, Decl(typeGuardByEqualityCheck.ts, 0, 0))
3131
}
3232

@@ -35,23 +35,23 @@ interface FooNode extends BaseNode {
3535
>BaseNode : Symbol(BaseNode, Decl(typeGuardByEqualityCheck.ts, 10, 1))
3636

3737
kind: FooDiscriminator;
38-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 16, 36))
38+
>kind : Symbol(FooNode.kind, Decl(typeGuardByEqualityCheck.ts, 16, 36))
3939
>FooDiscriminator : Symbol(FooDiscriminator, Decl(typeGuardByEqualityCheck.ts, 2, 1))
4040

4141
foo: string;
42-
>foo : Symbol(foo, Decl(typeGuardByEqualityCheck.ts, 17, 24))
42+
>foo : Symbol(FooNode.foo, Decl(typeGuardByEqualityCheck.ts, 17, 24))
4343
}
4444

4545
interface BarNode extends BaseNode {
4646
>BarNode : Symbol(BarNode, Decl(typeGuardByEqualityCheck.ts, 19, 1))
4747
>BaseNode : Symbol(BaseNode, Decl(typeGuardByEqualityCheck.ts, 10, 1))
4848

4949
kind: BarDiscriminator;
50-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 21, 36))
50+
>kind : Symbol(BarNode.kind, Decl(typeGuardByEqualityCheck.ts, 21, 36))
5151
>BarDiscriminator : Symbol(BarDiscriminator, Decl(typeGuardByEqualityCheck.ts, 6, 1))
5252

5353
bar: string;
54-
>bar : Symbol(bar, Decl(typeGuardByEqualityCheck.ts, 22, 24))
54+
>bar : Symbol(BarNode.bar, Decl(typeGuardByEqualityCheck.ts, 22, 24))
5555
}
5656

5757
let a: FooDiscriminator;
@@ -124,40 +124,40 @@ interface Thing {
124124
>Thing : Symbol(Thing, Decl(typeGuardByEqualityCheck.ts, 47, 21))
125125

126126
kind: string;
127-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 49, 17))
127+
>kind : Symbol(Thing.kind, Decl(typeGuardByEqualityCheck.ts, 49, 17))
128128
}
129129
interface FooThing extends Thing {
130130
>FooThing : Symbol(FooThing, Decl(typeGuardByEqualityCheck.ts, 51, 1))
131131
>Thing : Symbol(Thing, Decl(typeGuardByEqualityCheck.ts, 47, 21))
132132

133133
kind: "foo";
134-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 52, 34))
134+
>kind : Symbol(FooThing.kind, Decl(typeGuardByEqualityCheck.ts, 52, 34))
135135

136136
foo: string;
137-
>foo : Symbol(foo, Decl(typeGuardByEqualityCheck.ts, 53, 13))
137+
>foo : Symbol(FooThing.foo, Decl(typeGuardByEqualityCheck.ts, 53, 13))
138138
}
139139
interface BarThing extends Thing {
140140
>BarThing : Symbol(BarThing, Decl(typeGuardByEqualityCheck.ts, 55, 1))
141141
>Thing : Symbol(Thing, Decl(typeGuardByEqualityCheck.ts, 47, 21))
142142

143143
kind: "bar";
144-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 56, 34))
144+
>kind : Symbol(BarThing.kind, Decl(typeGuardByEqualityCheck.ts, 56, 34))
145145

146146
bar: string;
147-
>bar : Symbol(bar, Decl(typeGuardByEqualityCheck.ts, 57, 13))
147+
>bar : Symbol(BarThing.bar, Decl(typeGuardByEqualityCheck.ts, 57, 13))
148148
}
149149
interface FooBarThing extends Thing {
150150
>FooBarThing : Symbol(FooBarThing, Decl(typeGuardByEqualityCheck.ts, 59, 1))
151151
>Thing : Symbol(Thing, Decl(typeGuardByEqualityCheck.ts, 47, 21))
152152

153153
kind: "foobar";
154-
>kind : Symbol(kind, Decl(typeGuardByEqualityCheck.ts, 60, 37))
154+
>kind : Symbol(FooBarThing.kind, Decl(typeGuardByEqualityCheck.ts, 60, 37))
155155

156156
foo: string;
157-
>foo : Symbol(foo, Decl(typeGuardByEqualityCheck.ts, 61, 16))
157+
>foo : Symbol(FooBarThing.foo, Decl(typeGuardByEqualityCheck.ts, 61, 16))
158158

159159
bar: string;
160-
>bar : Symbol(bar, Decl(typeGuardByEqualityCheck.ts, 62, 13))
160+
>bar : Symbol(FooBarThing.bar, Decl(typeGuardByEqualityCheck.ts, 62, 13))
161161
}
162162

163163
let gg: FooThing | BarThing | FooBarThing;

0 commit comments

Comments
 (0)
Please sign in to comment.