From 20d8a94d66ce467053bce183b0a1e0538a1ea96d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 25 Feb 2016 03:32:15 -0500 Subject: [PATCH 01/18] Remove check narrowing only certain types, add test showing issues with this --- .../typeGuardNarrowsPrimitiveIntersection.js | 38 ++++++++++ ...eGuardNarrowsPrimitiveIntersection.symbols | 70 +++++++++++++++++ ...ypeGuardNarrowsPrimitiveIntersection.types | 76 +++++++++++++++++++ .../typeGuardNarrowsPrimitiveIntersection.ts | 21 +++++ 4 files changed, 205 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js new file mode 100644 index 0000000000000..a4cba6f4ab5c1 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js @@ -0,0 +1,38 @@ +//// [typeGuardNarrowsPrimitiveIntersection.ts] +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} + + +//// [typeGuardNarrowsPrimitiveIntersection.js] +var value; +if (isNonBlank(value)) { + doThis(value); +} +else { + doThat(value); +} +if (isNonBlank2(value)) { + doThis2(value); +} +else { + doThat2(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols new file mode 100644 index 0000000000000..da507fb94c5dc --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) +>__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12)) + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThis(value: string & Tag): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +if (isNonBlank(value)) { +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + + +const enum Tag2 {} +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThis2(value: string & Tag2): void; +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThat2(value: string) : void; +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25)) + +if (isNonBlank2(value)) { +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis2(value); +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat2(value); +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types new file mode 100644 index 0000000000000..478363669b461 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : { __tag: any; } +>__tag : any + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string +>value : any +>Tag : { __tag: any; } + +declare function doThis(value: string & Tag): void; +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } +>Tag : { __tag: any; } + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isNonBlank(value)) { +>isNonBlank(value) : boolean +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + +const enum Tag2 {} +>Tag2 : Tag2 + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string +>value : any +>Tag2 : Tag2 + +declare function doThis2(value: string & Tag2): void; +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 +>Tag2 : Tag2 + +declare function doThat2(value: string) : void; +>doThat2 : (value: string) => void +>value : string + +if (isNonBlank2(value)) { +>isNonBlank2(value) : boolean +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string + + doThis2(value); +>doThis2(value) : void +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 + +} else { + doThat2(value); +>doThat2(value) : void +>doThat2 : (value: string) => void +>value : string +} + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts new file mode 100644 index 0000000000000..376a78275c83b --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts @@ -0,0 +1,21 @@ +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} From a2feb0ee7dc07aad3ef9d05e2ea151c547fd460d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 11 Mar 2016 02:24:26 -0500 Subject: [PATCH 02/18] string literal case test --- .../typeGuardNarrowsToLiteralType.js | 21 +++++++++++ .../typeGuardNarrowsToLiteralType.symbols | 32 +++++++++++++++++ .../typeGuardNarrowsToLiteralType.types | 35 +++++++++++++++++++ .../typeGuardNarrowsToLiteralType.ts | 10 ++++++ 4 files changed, 98 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.js create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js new file mode 100644 index 0000000000000..fdd2ee3fb7f75 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralType.ts] +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralType.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols new file mode 100644 index 0000000000000..0d0ddc5f00dc1 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) + +declare function doThis(value: "foo"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types new file mode 100644 index 0000000000000..9835206deb9f4 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : (value: string) => value is "foo" +>value : string +>value : any + +declare function doThis(value: "foo"): void; +>doThis : (value: "foo") => void +>value : "foo" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo") => void +>value : "foo" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts new file mode 100644 index 0000000000000..3b7d5bba21ab7 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + From 92bf91dd581b98e9c9fd0c8d69dae6132af762ef Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:14:00 -0400 Subject: [PATCH 03/18] Reconcile fix with CFA work --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6a42dc43d5159..ca0d61c810603 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7555,7 +7555,7 @@ namespace ts { } function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { + if (type.flags & TypeFlags.Defaultable || !isNarrowableReference(reference)) { return type; } const leftmostNode = getLeftmostIdentifierOrThis(reference); @@ -7975,7 +7975,7 @@ namespace ts { const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && !(type.flags & TypeFlags.Narrowable)) { + if (defaultsToDeclaredType && type.flags & TypeFlags.Defaultable) { return type; } const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d0d411a710eb3..5f181bc982e77 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2175,7 +2175,13 @@ namespace ts { ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, - Narrowable = Any | ObjectType | Union | TypeParameter, + + // 'Defaultable' types are types where narrowing reverts to the original type, rather than actually narrow. + // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep + // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) + // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing + // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) + Defaultable = Void, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, /* @internal */ From 73d4aaef46eea2323587edb650678c2fe70aa170 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:17:40 -0400 Subject: [PATCH 04/18] Defaultable -> NotNarrowable to align with use --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca0d61c810603..2e2f59f6c155e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7555,7 +7555,7 @@ namespace ts { } function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (type.flags & TypeFlags.Defaultable || !isNarrowableReference(reference)) { + if (type.flags & TypeFlags.NotNarrowable || !isNarrowableReference(reference)) { return type; } const leftmostNode = getLeftmostIdentifierOrThis(reference); @@ -7975,7 +7975,7 @@ namespace ts { const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && type.flags & TypeFlags.Defaultable) { + if (defaultsToDeclaredType && type.flags & TypeFlags.NotNarrowable) { return type; } const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5f181bc982e77..47f9d1b752e55 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2176,12 +2176,12 @@ namespace ts { UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, - // 'Defaultable' types are types where narrowing reverts to the original type, rather than actually narrow. + // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow. // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) - Defaultable = Void, + NotNarrowable = Void, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, /* @internal */ From 70733da2f2013ef786486d64a6dce7efae504298 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:23:54 -0400 Subject: [PATCH 05/18] Missed a defaultable in comments --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 47f9d1b752e55..044cf3f4e8b97 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2178,7 +2178,7 @@ namespace ts { // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow. // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep - // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) + // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value) // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) NotNarrowable = Void, From a80529b9899ebe105f6e72a176f4bdf6c5eb1586 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:37:00 -0400 Subject: [PATCH 06/18] Add test for narrowing to unions of string literals --- .../typeGuardNarrowsToLiteralTypeUnion.js | 21 +++++++++++ ...typeGuardNarrowsToLiteralTypeUnion.symbols | 32 +++++++++++++++++ .../typeGuardNarrowsToLiteralTypeUnion.types | 35 +++++++++++++++++++ .../typeGuardNarrowsToLiteralTypeUnion.ts | 10 ++++++ 4 files changed, 98 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js new file mode 100644 index 0000000000000..715b362c8e0c2 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralTypeUnion.ts] +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralTypeUnion.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols new file mode 100644 index 0000000000000..356fa06a06c4a --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) + +declare function doThis(value: "foo" | "bar"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types new file mode 100644 index 0000000000000..321a8861d55f5 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : (value: string) => value is "foo" | "bar" +>value : string +>value : any + +declare function doThis(value: "foo" | "bar"): void; +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" | "bar" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts new file mode 100644 index 0000000000000..8f4726160f4b2 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + From 9a620bf616e1f6fac6d035ce43ad690455fa84a6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 1 Jun 2016 11:39:22 -0700 Subject: [PATCH 07/18] Actually merge from master --- src/compiler/checker.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 52e70941408ef..ef11dc904ea19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8156,23 +8156,12 @@ namespace ts { return type; } const declaration = localOrExportSymbol.valueDeclaration; -<<<<<<< HEAD - const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || - declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && type.flags & TypeFlags.NotNarrowable) { - return type; - } - const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); - if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { -======= const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol); const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { ->>>>>>> master error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; From 0e96c5eaf134c5317febf417044a638b4ce6d70e Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 1 Jun 2016 22:57:25 -0700 Subject: [PATCH 08/18] Run fixupParentReferences when parsing isolated jsDocComment --- src/compiler/parser.ts | 21 +++++++++++++++++- ...tacticClassificationForJSDocTemplateTag.ts | 22 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6f042627c6ba4..13f16f3848acd 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -440,7 +440,26 @@ namespace ts { /* @internal */ export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) { - return Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length); + const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length); + if (result.jsDocComment) { + // because the jsDocComment was parsed out of the source file, it might + // not be covered by the fixupParentReferences. + let parentNode: Node = result.jsDocComment; + forEachChild(result.jsDocComment, visitNode); + + function visitNode(n: Node): void { + if (n.parent !== parentNode) { + n.parent = parentNode; + + const saveParent = parentNode; + parentNode = n; + forEachChild(n, visitNode); + parentNode = saveParent; + } + } + } + + return result; } /* @internal */ diff --git a/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts new file mode 100644 index 0000000000000..c3368207d2cc2 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts @@ -0,0 +1,22 @@ +/// + +/////** @template T */ +////function ident: T { +////} + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/** "), + c.punctuation("@"), + c.docCommentTagName("template"), + c.typeParameterName("T"), + c.comment(" */"), + c.keyword("function"), + c.identifier("ident"), + c.punctuation("<"), + c.typeParameterName("T"), + c.punctuation(">"), + c.punctuation(":"), + c.identifier("T"), + c.punctuation("{"), + c.punctuation("}")); From 92177bee913eb7d27b256d421f590290963e4c11 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 2 Jun 2016 00:03:10 -0700 Subject: [PATCH 09/18] initial revision of unit test support for project system in tsserver --- Jakefile.js | 3 +- src/server/editorServices.ts | 2 +- .../cases/unittests/tsserverProjectSystem.ts | 294 ++++++++++++++++++ 3 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 tests/cases/unittests/tsserverProjectSystem.ts diff --git a/Jakefile.js b/Jakefile.js index 2f0ce4aa399cd..c4f56c559f364 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -153,7 +153,8 @@ var harnessSources = harnessCoreSources.concat([ "tsconfigParsing.ts", "commandLineParsing.ts", "convertCompilerOptionsFromJson.ts", - "convertTypingOptionsFromJson.ts" + "convertTypingOptionsFromJson.ts", + "tsserverProjectSystem.ts" ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 5502f74408e03..8eae43b503d3f 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1138,7 +1138,7 @@ namespace ts.server { else { this.log("No config files found."); } - return {}; + return configFileName ? { configFileName } : {}; } /** diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts new file mode 100644 index 0000000000000..c69821ced5f98 --- /dev/null +++ b/tests/cases/unittests/tsserverProjectSystem.ts @@ -0,0 +1,294 @@ +/// + +namespace ts { + function notImplemented(): any { + throw new Error("Not yet implemented"); + } + + const nullLogger: server.Logger = { + close: () => void 0, + isVerbose: () => void 0, + loggingEnabled: () => false, + perftrc: () => void 0, + info: () => void 0, + startGroup: () => void 0, + endGroup: () => void 0, + msg: () => void 0 + }; + + const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); + + function getExecutingFilePathFromLibFile(libFile: FileOrFolder): string { + return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); + } + + interface FileOrFolder { + path: string; + content?: string; + } + + interface FSEntry { + path: Path; + fullPath: string; + } + + interface File extends FSEntry { + content: string; + } + + interface Folder extends FSEntry { + entries: FSEntry[]; + } + + function isFolder(s: FSEntry): s is Folder { + return isArray((s).entries); + } + + function isFile(s: FSEntry): s is File { + return typeof (s).content === "string"; + } + + function addFolder(fullPath: string, toPath: (s: string) => Path, fs: FileMap): Folder { + const path = toPath(fullPath); + if (fs.contains(path)) { + Debug.assert(isFolder(fs.get(path))); + return (fs.get(path)); + } + + const entry: Folder = { path, entries: [], fullPath }; + fs.set(path, entry); + + const baseFullPath = getDirectoryPath(fullPath); + if (fullPath !== baseFullPath) { + addFolder(baseFullPath, toPath, fs).entries.push(entry); + } + + return entry; + } + + function sizeOfMap(map: Map): number { + let n = 0; + for (const name in map) { + if (hasProperty(map, name)) { + n++; + } + } + return n; + } + + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`); + for (const name of expectedKeys) { + assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`); + } + } + + function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { + assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); + for (const f of expectedFileNames) { + assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`); + } + } + + function readDirectory(folder: FSEntry, ext: string, excludes: Path[], result: string[]): void { + if (!folder || !isFolder(folder) || contains(excludes, folder.path)) { + return; + } + for (const entry of folder.entries) { + if (contains(excludes, entry.path)) { + continue; + } + if (isFolder(entry)) { + readDirectory(entry, ext, excludes, result); + } + else if (fileExtensionIs(entry.path, ext)) { + result.push(entry.fullPath); + } + } + } + + class TestServerHost implements server.ServerHost { + args: string[] = []; + newLine: "\n"; + + private fs: ts.FileMap; + private getCanonicalFileName: (s: string) => string; + private toPath: (f: string) => Path; + readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; + readonly watchedFiles: Map = {}; + + constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) { + this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); + + this.reloadFS(fileOrFolderList); + } + + reloadFS(filesOrFolders: FileOrFolder[]) { + this.fs = createFileMap(); + for (const fileOrFolder of filesOrFolders) { + const path = this.toPath(fileOrFolder.path); + const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); + if (typeof fileOrFolder.content === "string") { + const entry = { path, content: fileOrFolder.content, fullPath }; + this.fs.set(path, entry); + addFolder(getDirectoryPath(fullPath), this.toPath, this.fs).entries.push(entry); + } + else { + addFolder(fullPath, this.toPath, this.fs); + } + } + } + + fileExists(s: string) { + const path = this.toPath(s); + return this.fs.contains(path) && isFile(this.fs.get(path)); + }; + + directoryExists(s: string) { + const path = this.toPath(s); + return this.fs.contains(path) && isFolder(this.fs.get(path)); + } + + getDirectories(s: string) { + const path = this.toPath(s); + if (!this.fs.contains(path)) { + return []; + } + else { + const entry = this.fs.get(path); + return isFolder(entry) ? map(entry.entries, x => getBaseFileName(x.fullPath)) : []; + } + } + + readDirectory(path: string, ext: string, excludes: string[]): string[] { + const result: string[] = []; + readDirectory(this.fs.get(this.toPath(path)), ext, map(excludes, e => toPath(e, path, this.getCanonicalFileName)), result); + return result; + } + + watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { + const path = this.toPath(directoryName); + const callbacks = lookUp(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); + callbacks.push({ cb: callback, recursive }); + return { + referenceCount: 0, + directoryName, + close: () => { + for (let i = 0; i < callbacks.length; i++) { + if (callbacks[i].cb === callback) { + callbacks.splice(i, 1); + break; + } + } + if (!callbacks.length) { + delete this.watchedDirectories[path]; + } + } + }; + } + + watchFile(fileName: string, callback: FileWatcherCallback) { + const path = this.toPath(fileName); + const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []); + callbacks.push(callback); + return { + close: () => { + const i = callbacks.indexOf(callback); + callbacks.splice(i, 1); + if (!callbacks.length) { + delete this.watchedFiles[path]; + } + } + }; + } + + // TOOD: record and invoke callbacks to simulate timer events + readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0; + readonly clearTimeout = (timeoutId: any): void => void 0; + readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content; + readonly resolvePath = (s: string) => s; + readonly getExecutingFilePath = () => this.executingFilePath; + readonly getCurrentDirectory = () => this.currentDirectory; + readonly writeFile = (path: string, content: string) => notImplemented(); + readonly write = (s: string) => notImplemented(); + readonly createDirectory = (s: string) => notImplemented(); + readonly exit = () => notImplemented(); + } + + describe("tsserver project system:", () => { + it("create inferred project", () => { + const appFile: FileOrFolder = { + path: "/a/b/c/app.ts", + content: ` + import {f} from "./module" + console.log(f) + ` + }; + const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + const moduleFile: FileOrFolder = { + path: "/a/b/c/module.d.ts", + content: `export let x: number` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [appFile, moduleFile, libFile]); + const projectService = new server.ProjectService(host, nullLogger); + const { configFileName } = projectService.openClientFile(appFile.path); + + assert(!configFileName, `should not find config, got: '${configFileName}`); + assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project"); + assert.equal(projectService.configuredProjects.length, 0, "expected no configured project"); + + const project = projectService.inferredProjects[0]; + + checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]); + checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]); + }); + + it("create configured project without file list", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: ` + { + "compilerOptions": {}, + "exclude": [ + "e" + ] + }` + }; + const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + const file1: FileOrFolder = { + path: "/a/b/c/f1.ts", + content: "let x = 1" + }; + const file2: FileOrFolder = { + path: "/a/b/d/f2.ts", + content: "let y = 1" + }; + const file3: FileOrFolder = { + path: "/a/b/e/f3.ts", + content: "let z = 1" + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]); + const projectService = new server.ProjectService(host, nullLogger); + const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); + + assert(configFileName, "should find config file"); + assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); + assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project"); + assert.equal(projectService.configuredProjects.length, 1, "expected one configured project"); + + const project = projectService.configuredProjects[0]; + checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]); + checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]); + + checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open + checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]); + }); + }); +} \ No newline at end of file From 251723826962cc3fa3cc2d766c17dc197099cce3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:32:14 -0700 Subject: [PATCH 10/18] Add non-widening forms of null and undefined --- src/compiler/checker.ts | 37 ++++++++++++++++++++----------------- src/compiler/types.ts | 6 +++--- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06ef246340252..6166234dcf4d3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -110,16 +110,17 @@ namespace ts { const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); - const nullableWideningFlags = strictNullChecks ? 0 : TypeFlags.ContainsUndefinedOrNull; const anyType = createIntrinsicType(TypeFlags.Any, "any"); const stringType = createIntrinsicType(TypeFlags.String, "string"); const numberType = createIntrinsicType(TypeFlags.Number, "number"); const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean"); const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); const voidType = createIntrinsicType(TypeFlags.Void, "void"); - const undefinedType = createIntrinsicType(TypeFlags.Undefined | nullableWideningFlags, "undefined"); - const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null"); - const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined"); + const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); + const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); + const nullType = createIntrinsicType(TypeFlags.Null, "null"); + const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null"); + const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); const unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); const neverType = createIntrinsicType(TypeFlags.Never, "never"); @@ -3405,7 +3406,7 @@ namespace ts { error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); return type.resolvedBaseConstructorType = unknownType; } - if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) { + if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); return type.resolvedBaseConstructorType = unknownType; } @@ -5011,6 +5012,7 @@ namespace ts { containsAny?: boolean; containsUndefined?: boolean; containsNull?: boolean; + containsNonWideningType?: boolean; } function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) { @@ -5021,6 +5023,7 @@ namespace ts { if (type.flags & TypeFlags.Any) typeSet.containsAny = true; if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true; if (type.flags & TypeFlags.Null) typeSet.containsNull = true; + if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true; } else if (type !== neverType && !contains(typeSet, type)) { typeSet.push(type); @@ -5081,8 +5084,8 @@ namespace ts { removeSubtypes(typeSet); } if (typeSet.length === 0) { - return typeSet.containsNull ? nullType : - typeSet.containsUndefined ? undefinedType : + return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType : + typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType : neverType; } else if (typeSet.length === 1) { @@ -5882,7 +5885,7 @@ namespace ts { if (!(target.flags & TypeFlags.Never)) { if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True; if (source.flags & TypeFlags.Undefined) { - if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True; + if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True; } if (source.flags & TypeFlags.Null) { if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True; @@ -6972,7 +6975,7 @@ namespace ts { if (type.flags & TypeFlags.ObjectLiteral) { for (const p of getPropertiesOfObjectType(type)) { const t = getTypeOfSymbol(p); - if (t.flags & TypeFlags.ContainsUndefinedOrNull) { + if (t.flags & TypeFlags.ContainsWideningType) { if (!reportWideningErrorsInType(t)) { error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t))); } @@ -7019,7 +7022,7 @@ namespace ts { } function reportErrorsFromWidening(declaration: Declaration, type: Type) { - if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) { + if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) { // Report implicit any error within type if possible, otherwise report error on declaration if (!reportWideningErrorsInType(type)) { reportImplicitAnyError(declaration, type); @@ -8311,7 +8314,7 @@ namespace ts { const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); - return baseConstructorType === nullType; + return baseConstructorType === nullWideningType; } function checkThisExpression(node: Node): Type { @@ -9202,7 +9205,7 @@ namespace ts { } } } - return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType); + return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType); } function isNumericName(name: DeclarationName): boolean { @@ -12002,7 +12005,7 @@ namespace ts { function checkVoidExpression(node: VoidExpression): Type { checkExpression(node.expression); - return undefinedType; + return undefinedWideningType; } function checkAwaitExpression(node: AwaitExpression): Type { @@ -12409,7 +12412,7 @@ namespace ts { case SyntaxKind.InKeyword: return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: - return addNullableKind(rightType, getNullableKind(leftType)); + return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType; case SyntaxKind.BarBarToken: return getUnionType([getNonNullableType(leftType), rightType]); case SyntaxKind.EqualsToken: @@ -12676,7 +12679,7 @@ namespace ts { case SyntaxKind.SuperKeyword: return checkSuperExpression(node); case SyntaxKind.NullKeyword: - return nullType; + return nullWideningType; case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: return booleanType; @@ -12734,7 +12737,7 @@ namespace ts { case SyntaxKind.SpreadElementExpression: return checkSpreadElementExpression(node, contextualMapper); case SyntaxKind.OmittedExpression: - return undefinedType; + return undefinedWideningType; case SyntaxKind.YieldExpression: return checkYieldExpression(node); case SyntaxKind.JsxExpression: @@ -17648,7 +17651,7 @@ namespace ts { // Setup global builtins addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0); - getSymbolLinks(undefinedSymbol).type = undefinedType; + getSymbolLinks(undefinedSymbol).type = undefinedWideningType; getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments"); getSymbolLinks(unknownSymbol).type = unknownType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c7e14a1003108..4c0e7a036dbf4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2197,7 +2197,7 @@ namespace ts { /* @internal */ FreshObjectLiteral = 0x00100000, // Fresh object literal type /* @internal */ - ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type + ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type /* @internal */ ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type /* @internal */ @@ -2220,9 +2220,9 @@ namespace ts { StructuredType = ObjectType | Union | Intersection, Narrowable = Any | ObjectType | Union | TypeParameter, /* @internal */ - RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, + RequiresWidening = ContainsWideningType | ContainsObjectLiteral, /* @internal */ - PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType + PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType } export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; From 5f3f2d302fbbb5fd95c81534944553221914a342 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:47:37 -0700 Subject: [PATCH 11/18] Create separate control flows for property declarations with initializers --- src/compiler/binder.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5fdfc7f02be42..8c48d984b7853 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1142,6 +1142,8 @@ namespace ts { case SyntaxKind.ModuleBlock: return ContainerFlags.IsControlFlowContainer; + case SyntaxKind.PropertyDeclaration: + return (node).initializer ? ContainerFlags.IsControlFlowContainer : 0; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: From 706683d51b90ef8b67b52db0e21182c0eaba816d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:54:27 -0700 Subject: [PATCH 12/18] Add regression test --- .../controlFlowPropertyDeclarations.js | 291 +++++++++++++ .../controlFlowPropertyDeclarations.symbols | 288 +++++++++++++ .../controlFlowPropertyDeclarations.types | 383 ++++++++++++++++++ .../controlFlowPropertyDeclarations.ts | 148 +++++++ 4 files changed, 1110 insertions(+) create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.js create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.symbols create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.types create mode 100644 tests/cases/compiler/controlFlowPropertyDeclarations.ts diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.js b/tests/baselines/reference/controlFlowPropertyDeclarations.js new file mode 100644 index 0000000000000..748f63ea858da --- /dev/null +++ b/tests/baselines/reference/controlFlowPropertyDeclarations.js @@ -0,0 +1,291 @@ +//// [controlFlowPropertyDeclarations.ts] +// Repro from ##8913 + +declare var require:any; + +var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig'); + +// Populate property map with ReactJS's attribute and property mappings +// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr +for (var propname in HTMLDOMPropertyConfig.Properties) { + if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) { + continue; + } + + var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase(); +} + +/** + * Repeats a string a certain number of times. + * Also: the future is bright and consists of native string repetition: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat + * + * @param {string} string String to repeat + * @param {number} times Number of times to repeat string. Integer. + * @see http://jsperf.com/string-repeater/2 + */ +function repeatString(string, times) { + if (times === 1) { + return string; + } + if (times < 0) { throw new Error(); } + var repeated = ''; + while (times) { + if (times & 1) { + repeated += string; + } + if (times >>= 1) { + string += string; + } + } + return repeated; +} + +/** + * Determine if the string ends with the specified substring. + * + * @param {string} haystack String to search in + * @param {string} needle String to search for + * @return {boolean} + */ +function endsWith(haystack, needle) { + return haystack.slice(-needle.length) === needle; +} + +/** + * Trim the specified substring off the string. If the string does not end + * with the specified substring, this is a no-op. + * + * @param {string} haystack String to search in + * @param {string} needle String to search for + * @return {string} + */ +function trimEnd(haystack, needle) { + return endsWith(haystack, needle) + ? haystack.slice(0, -needle.length) + : haystack; +} + +/** + * Convert a hyphenated string to camelCase. + */ +function hyphenToCamelCase(string) { + return string.replace(/-(.)/g, function(match, chr) { + return chr.toUpperCase(); + }); +} + +/** + * Determines if the specified string consists entirely of whitespace. + */ +function isEmpty(string) { + return !/[^\s]/.test(string); +} + +/** + * Determines if the CSS value can be converted from a + * 'px' suffixed string to a numeric value + * + * @param {string} value CSS property value + * @return {boolean} + */ +function isConvertiblePixelValue(value) { + return /^\d+px$/.test(value); +} + +export class HTMLtoJSX { + private output: string; + private level: number; + private _inPreTag: boolean; + + + /** + * Handles processing of the specified text node + * + * @param {TextNode} node + */ + _visitText = (node) => { + var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase(); + if (parentTag === 'textarea' || parentTag === 'style') { + // Ignore text content of textareas and styles, as it will have already been moved + // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively. + return; + } + + var text = '' + + if (this._inPreTag) { + // If this text is contained within a
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+        .replace(/\r/g, '')
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+          return '{' + JSON.stringify(whitespace) + '}';
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+      }
+    }
+    this.output += text;
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+  styles = {};
+  toJSXString = () => {
+    for (var key in this.styles) {
+      if (!this.styles.hasOwnProperty(key)) {
+      }
+    }
+  }
+}
+
+//// [controlFlowPropertyDeclarations.js]
+// Repro from ##8913
+"use strict";
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+    if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+        continue;
+    }
+    var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+}
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+    if (times === 1) {
+        return string;
+    }
+    if (times < 0) {
+        throw new Error();
+    }
+    var repeated = '';
+    while (times) {
+        if (times & 1) {
+            repeated += string;
+        }
+        if (times >>= 1) {
+            string += string;
+        }
+    }
+    return repeated;
+}
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+    return haystack.slice(-needle.length) === needle;
+}
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+    return endsWith(haystack, needle)
+        ? haystack.slice(0, -needle.length)
+        : haystack;
+}
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+    return string.replace(/-(.)/g, function (match, chr) {
+        return chr.toUpperCase();
+    });
+}
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+    return !/[^\s]/.test(string);
+}
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+    return /^\d+px$/.test(value);
+}
+var HTMLtoJSX = (function () {
+    function HTMLtoJSX() {
+        var _this = this;
+        /**
+         * Handles processing of the specified text node
+         *
+         * @param {TextNode} node
+         */
+        this._visitText = function (node) {
+            var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+            if (parentTag === 'textarea' || parentTag === 'style') {
+                // Ignore text content of textareas and styles, as it will have already been moved
+                // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+                return;
+            }
+            var text = '';
+            if (_this._inPreTag) {
+                // If this text is contained within a 
, we need to ensure the JSX
+                // whitespace coalescing rules don't eat the whitespace. This means
+                // wrapping newlines and sequences of two or more spaces in variables.
+                text = text
+                    .replace(/\r/g, '')
+                    .replace(/( {2,}|\n|\t|\{|\})/g, function (whitespace) {
+                    return '{' + JSON.stringify(whitespace) + '}';
+                });
+            }
+            else {
+                // If there's a newline in the text, adjust the indent level
+                if (text.indexOf('\n') > -1) {
+                }
+            }
+            _this.output += text;
+        };
+    }
+    return HTMLtoJSX;
+}());
+exports.HTMLtoJSX = HTMLtoJSX;
+;
+/**
+ * Handles parsing of inline styles
+ */
+var StyleParser = (function () {
+    function StyleParser() {
+        var _this = this;
+        this.styles = {};
+        this.toJSXString = function () {
+            for (var key in _this.styles) {
+                if (!_this.styles.hasOwnProperty(key)) {
+                }
+            }
+        };
+    }
+    return StyleParser;
+}());
+exports.StyleParser = StyleParser;
diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.symbols b/tests/baselines/reference/controlFlowPropertyDeclarations.symbols
new file mode 100644
index 0000000000000..8e87d71ae331c
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyDeclarations.symbols
@@ -0,0 +1,288 @@
+=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
+// Repro from ##8913
+
+declare var require:any;
+>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+>mapFrom : Symbol(mapFrom, Decl(controlFlowPropertyDeclarations.ts, 13, 5))
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>propname.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+>repeatString : Symbol(repeatString, Decl(controlFlowPropertyDeclarations.ts, 14, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+  if (times === 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+    return string;
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+  }
+  if (times < 0) { throw new Error(); }
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+
+  var repeated = '';
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+
+  while (times) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+    if (times & 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+      repeated += string;
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+    }
+    if (times >>= 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+      string += string;
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+    }
+  }
+  return repeated;
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+
+  return haystack.slice(-needle.length) === needle;
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+>trimEnd : Symbol(trimEnd, Decl(controlFlowPropertyDeclarations.ts, 51, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+  return endsWith(haystack, needle)
+>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+    ? haystack.slice(0, -needle.length)
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+    : haystack;
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+>hyphenToCamelCase : Symbol(hyphenToCamelCase, Decl(controlFlowPropertyDeclarations.ts, 65, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
+
+  return string.replace(/-(.)/g, function(match, chr) {
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
+>match : Symbol(match, Decl(controlFlowPropertyDeclarations.ts, 71, 42))
+>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
+
+    return chr.toUpperCase();
+>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
+
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+>isEmpty : Symbol(isEmpty, Decl(controlFlowPropertyDeclarations.ts, 74, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
+
+   return !/[^\s]/.test(string);
+>/[^\s]/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+>isConvertiblePixelValue : Symbol(isConvertiblePixelValue, Decl(controlFlowPropertyDeclarations.ts, 81, 1))
+>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
+
+  return /^\d+px$/.test(value);
+>/^\d+px$/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
+}
+
+export class HTMLtoJSX {
+>HTMLtoJSX : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+
+    private output: string;
+>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+
+    private level: number;
+>level : Symbol(HTMLtoJSX.level, Decl(controlFlowPropertyDeclarations.ts, 95, 27))
+
+    private _inPreTag: boolean;
+>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+>_visitText : Symbol(HTMLtoJSX._visitText, Decl(controlFlowPropertyDeclarations.ts, 97, 31))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+
+    if (parentTag === 'textarea' || parentTag === 'style') {
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+
+    if (this._inPreTag) {
+>this._inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+>text        .replace(/\r/g, '')        .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>text        .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+
+        .replace(/\r/g, '')
+>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
+
+          return '{' + JSON.stringify(whitespace) + '}';
+>JSON.stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
+
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+>text.indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+>indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
+      }
+    }
+    this.output += text;
+>this.output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+>StyleParser : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+
+  styles = {};
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+
+  toJSXString = () => {
+>toJSXString : Symbol(StyleParser.toJSXString, Decl(controlFlowPropertyDeclarations.ts, 140, 14))
+
+    for (var key in this.styles) {
+>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
+>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+
+      if (!this.styles.hasOwnProperty(key)) {
+>this.styles.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
+>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
+>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
+      }
+    }
+  }
+}
diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.types b/tests/baselines/reference/controlFlowPropertyDeclarations.types
new file mode 100644
index 0000000000000..81c86e8625d04
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyDeclarations.types
@@ -0,0 +1,383 @@
+=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
+// Repro from ##8913
+
+declare var require:any;
+>require : any
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+>HTMLDOMPropertyConfig : any
+>require('react/lib/HTMLDOMPropertyConfig') : any
+>require : any
+>'react/lib/HTMLDOMPropertyConfig' : string
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+>propname : string
+>HTMLDOMPropertyConfig.Properties : any
+>HTMLDOMPropertyConfig : any
+>Properties : any
+
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+>!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : boolean
+>HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : any
+>HTMLDOMPropertyConfig.Properties.hasOwnProperty : any
+>HTMLDOMPropertyConfig.Properties : any
+>HTMLDOMPropertyConfig : any
+>Properties : any
+>hasOwnProperty : any
+>propname : string
+
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+>mapFrom : any
+>HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase() : any
+>HTMLDOMPropertyConfig.DOMAttributeNames[propname] : any
+>HTMLDOMPropertyConfig.DOMAttributeNames : any
+>HTMLDOMPropertyConfig : any
+>DOMAttributeNames : any
+>propname : string
+>propname.toLowerCase() : string
+>propname.toLowerCase : () => string
+>propname : string
+>toLowerCase : () => string
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+>repeatString : (string: any, times: any) => any
+>string : any
+>times : any
+
+  if (times === 1) {
+>times === 1 : boolean
+>times : any
+>1 : number
+
+    return string;
+>string : any
+  }
+  if (times < 0) { throw new Error(); }
+>times < 0 : boolean
+>times : any
+>0 : number
+>new Error() : Error
+>Error : ErrorConstructor
+
+  var repeated = '';
+>repeated : string
+>'' : string
+
+  while (times) {
+>times : any
+
+    if (times & 1) {
+>times & 1 : number
+>times : any
+>1 : number
+
+      repeated += string;
+>repeated += string : string
+>repeated : string
+>string : any
+    }
+    if (times >>= 1) {
+>times >>= 1 : number
+>times : any
+>1 : number
+
+      string += string;
+>string += string : any
+>string : any
+>string : any
+    }
+  }
+  return repeated;
+>repeated : string
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+>endsWith : (haystack: any, needle: any) => boolean
+>haystack : any
+>needle : any
+
+  return haystack.slice(-needle.length) === needle;
+>haystack.slice(-needle.length) === needle : boolean
+>haystack.slice(-needle.length) : any
+>haystack.slice : any
+>haystack : any
+>slice : any
+>-needle.length : number
+>needle.length : any
+>needle : any
+>length : any
+>needle : any
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+>trimEnd : (haystack: any, needle: any) => any
+>haystack : any
+>needle : any
+
+  return endsWith(haystack, needle)
+>endsWith(haystack, needle)    ? haystack.slice(0, -needle.length)    : haystack : any
+>endsWith(haystack, needle) : boolean
+>endsWith : (haystack: any, needle: any) => boolean
+>haystack : any
+>needle : any
+
+    ? haystack.slice(0, -needle.length)
+>haystack.slice(0, -needle.length) : any
+>haystack.slice : any
+>haystack : any
+>slice : any
+>0 : number
+>-needle.length : number
+>needle.length : any
+>needle : any
+>length : any
+
+    : haystack;
+>haystack : any
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+>hyphenToCamelCase : (string: any) => any
+>string : any
+
+  return string.replace(/-(.)/g, function(match, chr) {
+>string.replace(/-(.)/g, function(match, chr) {    return chr.toUpperCase();  }) : any
+>string.replace : any
+>string : any
+>replace : any
+>/-(.)/g : RegExp
+>function(match, chr) {    return chr.toUpperCase();  } : (match: any, chr: any) => any
+>match : any
+>chr : any
+
+    return chr.toUpperCase();
+>chr.toUpperCase() : any
+>chr.toUpperCase : any
+>chr : any
+>toUpperCase : any
+
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+>isEmpty : (string: any) => boolean
+>string : any
+
+   return !/[^\s]/.test(string);
+>!/[^\s]/.test(string) : boolean
+>/[^\s]/.test(string) : boolean
+>/[^\s]/.test : (string: string) => boolean
+>/[^\s]/ : RegExp
+>test : (string: string) => boolean
+>string : any
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+>isConvertiblePixelValue : (value: any) => boolean
+>value : any
+
+  return /^\d+px$/.test(value);
+>/^\d+px$/.test(value) : boolean
+>/^\d+px$/.test : (string: string) => boolean
+>/^\d+px$/ : RegExp
+>test : (string: string) => boolean
+>value : any
+}
+
+export class HTMLtoJSX {
+>HTMLtoJSX : HTMLtoJSX
+
+    private output: string;
+>output : string
+
+    private level: number;
+>level : number
+
+    private _inPreTag: boolean;
+>_inPreTag : boolean
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+>_visitText : (node: any) => void
+>(node) => {    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();    if (parentTag === 'textarea' || parentTag === 'style') {      // Ignore text content of textareas and styles, as it will have already been moved      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.      return;    }    var text = ''    if (this._inPreTag) {      // If this text is contained within a 
, we need to ensure the JSX      // whitespace coalescing rules don't eat the whitespace. This means      // wrapping newlines and sequences of two or more spaces in variables.      text = text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        });    } else {      // If there's a newline in the text, adjust the indent level      if (text.indexOf('\n') > -1) {      }    }    this.output += text;  } : (node: any) => void
+>node : any
+
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+>parentTag : any
+>node.parentNode && node.parentNode.tagName.toLowerCase() : any
+>node.parentNode : any
+>node : any
+>parentNode : any
+>node.parentNode.tagName.toLowerCase() : any
+>node.parentNode.tagName.toLowerCase : any
+>node.parentNode.tagName : any
+>node.parentNode : any
+>node : any
+>parentNode : any
+>tagName : any
+>toLowerCase : any
+
+    if (parentTag === 'textarea' || parentTag === 'style') {
+>parentTag === 'textarea' || parentTag === 'style' : boolean
+>parentTag === 'textarea' : boolean
+>parentTag : any
+>'textarea' : string
+>parentTag === 'style' : boolean
+>parentTag : any
+>'style' : string
+
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+>text : string
+>'' : string
+
+    if (this._inPreTag) {
+>this._inPreTag : boolean
+>this : this
+>_inPreTag : boolean
+
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+>text = text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        }) : string
+>text : string
+>text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        }) : string
+>text        .replace(/\r/g, '')        .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>text        .replace(/\r/g, '') : string
+>text        .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>text : string
+
+        .replace(/\r/g, '')
+>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>/\r/g : RegExp
+>'' : string
+
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>/( {2,}|\n|\t|\{|\})/g : RegExp
+>function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        } : (whitespace: string) => string
+>whitespace : string
+
+          return '{' + JSON.stringify(whitespace) + '}';
+>'{' + JSON.stringify(whitespace) + '}' : string
+>'{' + JSON.stringify(whitespace) : string
+>'{' : string
+>JSON.stringify(whitespace) : string
+>JSON.stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
+>JSON : JSON
+>stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
+>whitespace : string
+>'}' : string
+
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+>text.indexOf('\n') > -1 : boolean
+>text.indexOf('\n') : number
+>text.indexOf : (searchString: string, position?: number) => number
+>text : string
+>indexOf : (searchString: string, position?: number) => number
+>'\n' : string
+>-1 : number
+>1 : number
+      }
+    }
+    this.output += text;
+>this.output += text : string
+>this.output : string
+>this : this
+>output : string
+>text : string
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+>StyleParser : StyleParser
+
+  styles = {};
+>styles : {}
+>{} : {}
+
+  toJSXString = () => {
+>toJSXString : () => void
+>() => {    for (var key in this.styles) {      if (!this.styles.hasOwnProperty(key)) {      }    }  } : () => void
+
+    for (var key in this.styles) {
+>key : string
+>this.styles : {}
+>this : this
+>styles : {}
+
+      if (!this.styles.hasOwnProperty(key)) {
+>!this.styles.hasOwnProperty(key) : boolean
+>this.styles.hasOwnProperty(key) : boolean
+>this.styles.hasOwnProperty : (v: string) => boolean
+>this.styles : {}
+>this : this
+>styles : {}
+>hasOwnProperty : (v: string) => boolean
+>key : string
+      }
+    }
+  }
+}
diff --git a/tests/cases/compiler/controlFlowPropertyDeclarations.ts b/tests/cases/compiler/controlFlowPropertyDeclarations.ts
new file mode 100644
index 0000000000000..5a5e9fb96bfa0
--- /dev/null
+++ b/tests/cases/compiler/controlFlowPropertyDeclarations.ts
@@ -0,0 +1,148 @@
+// Repro from ##8913
+
+declare var require:any;
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+  if (times === 1) {
+    return string;
+  }
+  if (times < 0) { throw new Error(); }
+  var repeated = '';
+  while (times) {
+    if (times & 1) {
+      repeated += string;
+    }
+    if (times >>= 1) {
+      string += string;
+    }
+  }
+  return repeated;
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+  return haystack.slice(-needle.length) === needle;
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+  return endsWith(haystack, needle)
+    ? haystack.slice(0, -needle.length)
+    : haystack;
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+  return string.replace(/-(.)/g, function(match, chr) {
+    return chr.toUpperCase();
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+   return !/[^\s]/.test(string);
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+  return /^\d+px$/.test(value);
+}
+
+export class HTMLtoJSX {
+    private output: string;
+    private level: number;
+    private _inPreTag: boolean;
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+    if (parentTag === 'textarea' || parentTag === 'style') {
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+
+    if (this._inPreTag) {
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+        .replace(/\r/g, '')
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+          return '{' + JSON.stringify(whitespace) + '}';
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+      }
+    }
+    this.output += text;
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+  styles = {};
+  toJSXString = () => {
+    for (var key in this.styles) {
+      if (!this.styles.hasOwnProperty(key)) {
+      }
+    }
+  }
+}
\ No newline at end of file

From 20bab14224bf9f7a7228f416a61807b1f7f73e4a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Thu, 2 Jun 2016 09:39:47 -0700
Subject: [PATCH 13/18] Add tests

---
 .../reference/arrayLiteralWidened.js          | 15 ++++
 .../reference/arrayLiteralWidened.symbols     | 38 +++++++--
 .../reference/arrayLiteralWidened.types       | 29 +++++++
 .../reference/initializersWidened.js          | 42 +++++++++-
 .../reference/initializersWidened.symbols     | 55 ++++++++++++-
 .../reference/initializersWidened.types       | 77 ++++++++++++++++++-
 .../logicalAndOperatorWithEveryType.types     |  2 +-
 .../reference/objectLiteralWidened.js         | 40 +++++++++-
 .../reference/objectLiteralWidened.symbols    | 53 +++++++++++--
 .../reference/objectLiteralWidened.types      | 54 ++++++++++++-
 .../reference/strictNullChecksNoWidening.js   | 31 ++++++++
 .../strictNullChecksNoWidening.symbols        | 48 ++++++++++++
 .../strictNullChecksNoWidening.types          | 67 ++++++++++++++++
 .../widenedTypes/arrayLiteralWidened.ts       |  9 +++
 .../widenedTypes/initializersWidened.ts       | 24 +++++-
 .../widenedTypes/objectLiteralWidened.ts      | 22 +++++-
 .../strictNullChecksNoWidening.ts             | 17 ++++
 17 files changed, 584 insertions(+), 39 deletions(-)
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.js
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.symbols
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.types
 create mode 100644 tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts

diff --git a/tests/baselines/reference/arrayLiteralWidened.js b/tests/baselines/reference/arrayLiteralWidened.js
index a250c47849d57..5b7346b87381b 100644
--- a/tests/baselines/reference/arrayLiteralWidened.js
+++ b/tests/baselines/reference/arrayLiteralWidened.js
@@ -2,6 +2,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -12,11 +13,20 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
 
 
 //// [arrayLiteralWidened.js]
 // array literals are widened upon assignment according to their element type
 var a = []; // any[]
+var a = [, ,];
 var a = [null, null];
 var a = [undefined, undefined];
 var b = [[], [null, null]]; // any[][]
@@ -24,3 +34,8 @@ var b = [[], []];
 var b = [[undefined, undefined]];
 var c = [[[]]]; // any[][][]
 var c = [[[null]], [undefined]];
+// no widening when one or more elements are non-widening
+var x = undefined;
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/baselines/reference/arrayLiteralWidened.symbols b/tests/baselines/reference/arrayLiteralWidened.symbols
index ba058aeded114..48137812a5dfc 100644
--- a/tests/baselines/reference/arrayLiteralWidened.symbols
+++ b/tests/baselines/reference/arrayLiteralWidened.symbols
@@ -2,31 +2,53 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
+
+var a = [,,];
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [null, null];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [undefined, undefined];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var b = [[], [null, null]]; // any[][]
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[], []];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[undefined, undefined]];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var c = [[[]]]; // any[][][]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
 
 var c = [[[null]],[undefined]]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
+>undefined : Symbol(undefined)
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var d = [x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [undefined, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
 >undefined : Symbol(undefined)
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
 
diff --git a/tests/baselines/reference/arrayLiteralWidened.types b/tests/baselines/reference/arrayLiteralWidened.types
index 83db7046eed4a..6237cd79a7edb 100644
--- a/tests/baselines/reference/arrayLiteralWidened.types
+++ b/tests/baselines/reference/arrayLiteralWidened.types
@@ -5,6 +5,12 @@ var a = []; // any[]
 >a : any[]
 >[] : undefined[]
 
+var a = [,,];
+>a : any[]
+>[,,] : undefined[]
+> : undefined
+> : undefined
+
 var a = [null, null];
 >a : any[]
 >[null, null] : null[]
@@ -53,3 +59,26 @@ var c = [[[null]],[undefined]]
 >[undefined] : undefined[]
 >undefined : undefined
 
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : undefined
+>undefined : undefined
+
+var d = [x];
+>d : undefined[]
+>[x] : undefined[]
+>x : undefined
+
+var d = [, x];
+>d : undefined[]
+>[, x] : undefined[]
+> : undefined
+>x : undefined
+
+var d = [undefined, x];
+>d : undefined[]
+>[undefined, x] : undefined[]
+>undefined : undefined
+>x : undefined
+
diff --git a/tests/baselines/reference/initializersWidened.js b/tests/baselines/reference/initializersWidened.js
index fce0750a600c8..3954735ee6ede 100644
--- a/tests/baselines/reference/initializersWidened.js
+++ b/tests/baselines/reference/initializersWidened.js
@@ -1,10 +1,44 @@
 //// [initializersWidened.ts]
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
 
 //// [initializersWidened.js]
 // these are widened to any at the point of assignment
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+// these are not widened
+var x2;
+var y2;
+var x3 = null;
+var y3 = undefined;
+var z3 = void 0;
+// widen only when all constituents of union are widening
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
diff --git a/tests/baselines/reference/initializersWidened.symbols b/tests/baselines/reference/initializersWidened.symbols
index 625c066a0587e..252a248bec867 100644
--- a/tests/baselines/reference/initializersWidened.symbols
+++ b/tests/baselines/reference/initializersWidened.symbols
@@ -1,10 +1,57 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : Symbol(x, Decl(initializersWidened.ts, 2, 3))
+var x1 = null;
+>x1 : Symbol(x1, Decl(initializersWidened.ts, 2, 3))
 
-var y = undefined;
->y : Symbol(y, Decl(initializersWidened.ts, 3, 3))
+var y1 = undefined;
+>y1 : Symbol(y1, Decl(initializersWidened.ts, 3, 3))
 >undefined : Symbol(undefined)
 
+var z1 = void 0;
+>z1 : Symbol(z1, Decl(initializersWidened.ts, 4, 3))
+
+// these are not widened
+
+var x2: null;
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y2: undefined;
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var x3: null = null;
+>x3 : Symbol(x3, Decl(initializersWidened.ts, 11, 3))
+
+var y3: undefined = undefined;
+>y3 : Symbol(y3, Decl(initializersWidened.ts, 12, 3))
+>undefined : Symbol(undefined)
+
+var z3: undefined = void 0;
+>z3 : Symbol(z3, Decl(initializersWidened.ts, 13, 3))
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : Symbol(x4, Decl(initializersWidened.ts, 17, 3))
+
+var y4 = undefined || undefined;
+>y4 : Symbol(y4, Decl(initializersWidened.ts, 18, 3))
+>undefined : Symbol(undefined)
+>undefined : Symbol(undefined)
+
+var z4 = void 0 || void 0;
+>z4 : Symbol(z4, Decl(initializersWidened.ts, 19, 3))
+
+var x5 = null || x2;
+>x5 : Symbol(x5, Decl(initializersWidened.ts, 21, 3))
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y5 = undefined || y2;
+>y5 : Symbol(y5, Decl(initializersWidened.ts, 22, 3))
+>undefined : Symbol(undefined)
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var z5 = void 0 || y2;
+>z5 : Symbol(z5, Decl(initializersWidened.ts, 23, 3))
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
diff --git a/tests/baselines/reference/initializersWidened.types b/tests/baselines/reference/initializersWidened.types
index 157055892460a..766f859029af9 100644
--- a/tests/baselines/reference/initializersWidened.types
+++ b/tests/baselines/reference/initializersWidened.types
@@ -1,11 +1,80 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : any
+var x1 = null;
+>x1 : any
 >null : null
 
-var y = undefined;
->y : any
+var y1 = undefined;
+>y1 : any
 >undefined : undefined
 
+var z1 = void 0;
+>z1 : any
+>void 0 : undefined
+>0 : number
+
+// these are not widened
+
+var x2: null;
+>x2 : null
+>null : null
+
+var y2: undefined;
+>y2 : undefined
+
+var x3: null = null;
+>x3 : null
+>null : null
+>null : null
+
+var y3: undefined = undefined;
+>y3 : undefined
+>undefined : undefined
+
+var z3: undefined = void 0;
+>z3 : undefined
+>void 0 : undefined
+>0 : number
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : any
+>null || null : null
+>null : null
+>null : null
+
+var y4 = undefined || undefined;
+>y4 : any
+>undefined || undefined : undefined
+>undefined : undefined
+>undefined : undefined
+
+var z4 = void 0 || void 0;
+>z4 : any
+>void 0 || void 0 : undefined
+>void 0 : undefined
+>0 : number
+>void 0 : undefined
+>0 : number
+
+var x5 = null || x2;
+>x5 : null
+>null || x2 : null
+>null : null
+>x2 : null
+
+var y5 = undefined || y2;
+>y5 : undefined
+>undefined || y2 : undefined
+>undefined : undefined
+>y2 : undefined
+
+var z5 = void 0 || y2;
+>z5 : undefined
+>void 0 || y2 : undefined
+>void 0 : undefined
+>0 : number
+>y2 : undefined
+
diff --git a/tests/baselines/reference/logicalAndOperatorWithEveryType.types b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
index b2628eed9219f..bd913e94da58c 100644
--- a/tests/baselines/reference/logicalAndOperatorWithEveryType.types
+++ b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
@@ -623,7 +623,7 @@ var rj8 = a8 && undefined;
 
 var rj9 = null && undefined;
 >rj9 : any
->null && undefined : null
+>null && undefined : undefined
 >null : null
 >undefined : undefined
 
diff --git a/tests/baselines/reference/objectLiteralWidened.js b/tests/baselines/reference/objectLiteralWidened.js
index 4de228cb14a7f..98d79133540bd 100644
--- a/tests/baselines/reference/objectLiteralWidened.js
+++ b/tests/baselines/reference/objectLiteralWidened.js
@@ -1,29 +1,61 @@
 //// [objectLiteralWidened.ts]
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
 
 //// [objectLiteralWidened.js]
 // object literal properties are widened to any
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 };
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
 };
+// these are not widened
+var u = undefined;
+var n = null;
+var x2 = {
+    foo: n,
+    bar: u
+};
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
+};
diff --git a/tests/baselines/reference/objectLiteralWidened.symbols b/tests/baselines/reference/objectLiteralWidened.symbols
index 0bf077cd9d8a1..4b2a1b4a00185 100644
--- a/tests/baselines/reference/objectLiteralWidened.symbols
+++ b/tests/baselines/reference/objectLiteralWidened.symbols
@@ -1,22 +1,22 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : Symbol(x, Decl(objectLiteralWidened.ts, 2, 3))
+var x1 = {
+>x1 : Symbol(x1, Decl(objectLiteralWidened.ts, 2, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 10))
 
     bar: undefined
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 3, 14))
 >undefined : Symbol(undefined)
 }
 
-var y = {
->y : Symbol(y, Decl(objectLiteralWidened.ts, 7, 3))
+var y1 = {
+>y1 : Symbol(y1, Decl(objectLiteralWidened.ts, 7, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 10))
 
     bar: {
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 8, 14))
@@ -29,3 +29,44 @@ var y = {
 >undefined : Symbol(undefined)
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var n: null = null;
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+var x2 = {
+>x2 : Symbol(x2, Decl(objectLiteralWidened.ts, 20, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 20, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: u
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 21, 11))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+}
+
+var y2 = {
+>y2 : Symbol(y2, Decl(objectLiteralWidened.ts, 25, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 25, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: {
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 26, 11))
+
+        baz: n,
+>baz : Symbol(baz, Decl(objectLiteralWidened.ts, 27, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+        boo: u
+>boo : Symbol(boo, Decl(objectLiteralWidened.ts, 28, 15))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+    }
+}
diff --git a/tests/baselines/reference/objectLiteralWidened.types b/tests/baselines/reference/objectLiteralWidened.types
index 9f47e47795d56..66309202617d9 100644
--- a/tests/baselines/reference/objectLiteralWidened.types
+++ b/tests/baselines/reference/objectLiteralWidened.types
@@ -1,8 +1,8 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : { foo: any; bar: any; }
+var x1 = {
+>x1 : { foo: any; bar: any; }
 >{    foo: null,    bar: undefined} : { foo: null; bar: undefined; }
 
     foo: null,
@@ -14,8 +14,8 @@ var x = {
 >undefined : undefined
 }
 
-var y = {
->y : { foo: any; bar: { baz: any; boo: any; }; }
+var y1 = {
+>y1 : { foo: any; bar: { baz: any; boo: any; }; }
 >{    foo: null,    bar: {        baz: null,        boo: undefined    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
 
     foo: null,
@@ -35,3 +35,49 @@ var y = {
 >undefined : undefined
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : undefined
+>undefined : undefined
+
+var n: null = null;
+>n : null
+>null : null
+>null : null
+
+var x2 = {
+>x2 : { foo: null; bar: undefined; }
+>{    foo: n,    bar: u} : { foo: null; bar: undefined; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: u
+>bar : undefined
+>u : undefined
+}
+
+var y2 = {
+>y2 : { foo: null; bar: { baz: null; boo: undefined; }; }
+>{    foo: n,    bar: {        baz: n,        boo: u    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: {
+>bar : { baz: null; boo: undefined; }
+>{        baz: n,        boo: u    } : { baz: null; boo: undefined; }
+
+        baz: n,
+>baz : null
+>n : null
+
+        boo: u
+>boo : undefined
+>u : undefined
+    }
+}
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.js b/tests/baselines/reference/strictNullChecksNoWidening.js
new file mode 100644
index 0000000000000..ba26a04fc131a
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.js
@@ -0,0 +1,31 @@
+//// [strictNullChecksNoWidening.ts]
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
+
+
+//// [strictNullChecksNoWidening.js]
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.symbols b/tests/baselines/reference/strictNullChecksNoWidening.symbols
new file mode 100644
index 0000000000000..a23a0d2e926c0
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.symbols
@@ -0,0 +1,48 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : Symbol(a1, Decl(strictNullChecksNoWidening.ts, 1, 3))
+
+var a2 = undefined;
+>a2 : Symbol(a2, Decl(strictNullChecksNoWidening.ts, 2, 3))
+>undefined : Symbol(undefined)
+
+var a3 = void 0;
+>a3 : Symbol(a3, Decl(strictNullChecksNoWidening.ts, 3, 3))
+
+var b1 = [];
+>b1 : Symbol(b1, Decl(strictNullChecksNoWidening.ts, 5, 3))
+
+var b2 = [,];
+>b2 : Symbol(b2, Decl(strictNullChecksNoWidening.ts, 6, 3))
+
+var b3 = [undefined];
+>b3 : Symbol(b3, Decl(strictNullChecksNoWidening.ts, 7, 3))
+>undefined : Symbol(undefined)
+
+var b4 = [[], []];
+>b4 : Symbol(b4, Decl(strictNullChecksNoWidening.ts, 8, 3))
+
+var b5 = [[], [,]];
+>b5 : Symbol(b5, Decl(strictNullChecksNoWidening.ts, 9, 3))
+
+declare function f(x: T): T;
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>x : Symbol(x, Decl(strictNullChecksNoWidening.ts, 11, 22))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+
+var c1 = f(null);
+>c1 : Symbol(c1, Decl(strictNullChecksNoWidening.ts, 13, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
+var c2 = f(undefined);
+>c2 : Symbol(c2, Decl(strictNullChecksNoWidening.ts, 14, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>undefined : Symbol(undefined)
+
+var c3 = f([]);
+>c3 : Symbol(c3, Decl(strictNullChecksNoWidening.ts, 15, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.types b/tests/baselines/reference/strictNullChecksNoWidening.types
new file mode 100644
index 0000000000000..6dd2ee3fb4cfb
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.types
@@ -0,0 +1,67 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : null
+>null : null
+
+var a2 = undefined;
+>a2 : undefined
+>undefined : undefined
+
+var a3 = void 0;
+>a3 : undefined
+>void 0 : undefined
+>0 : number
+
+var b1 = [];
+>b1 : never[]
+>[] : never[]
+
+var b2 = [,];
+>b2 : undefined[]
+>[,] : undefined[]
+> : undefined
+
+var b3 = [undefined];
+>b3 : undefined[]
+>[undefined] : undefined[]
+>undefined : undefined
+
+var b4 = [[], []];
+>b4 : never[][]
+>[[], []] : never[][]
+>[] : never[]
+>[] : never[]
+
+var b5 = [[], [,]];
+>b5 : undefined[][]
+>[[], [,]] : undefined[][]
+>[] : never[]
+>[,] : undefined[]
+> : undefined
+
+declare function f(x: T): T;
+>f : (x: T) => T
+>T : T
+>x : T
+>T : T
+>T : T
+
+var c1 = f(null);
+>c1 : null
+>f(null) : null
+>f : (x: T) => T
+>null : null
+
+var c2 = f(undefined);
+>c2 : undefined
+>f(undefined) : undefined
+>f : (x: T) => T
+>undefined : undefined
+
+var c3 = f([]);
+>c3 : never[]
+>f([]) : never[]
+>f : (x: T) => T
+>[] : never[]
+
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
index 8af0a5842cc2e..05428422129c6 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
@@ -1,6 +1,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -11,3 +12,11 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
index e79cdc9e16834..2eeb96194b753 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
@@ -1,4 +1,24 @@
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
\ No newline at end of file
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
index cde44f9116856..8b51e526882e7 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
@@ -1,14 +1,32 @@
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
new file mode 100644
index 0000000000000..8f5b4709abf9e
--- /dev/null
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
@@ -0,0 +1,17 @@
+// @strictNullChecks: true
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);

From fb2df77a59453e72e3bbc92ab4bb9e5c68cb4a8a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Thu, 2 Jun 2016 10:47:47 -0700
Subject: [PATCH 14/18] Remove unused variable

---
 src/compiler/checker.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 6166234dcf4d3..b8f078ea9a56b 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -120,7 +120,6 @@ namespace ts {
         const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const nullType = createIntrinsicType(TypeFlags.Null, "null");
         const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
-        const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
         const neverType = createIntrinsicType(TypeFlags.Never, "never");
 

From d41ac8aa9a85203f413a52842975af4a4d4e09ec Mon Sep 17 00:00:00 2001
From: zhengbli 
Date: Thu, 2 Jun 2016 11:12:38 -0700
Subject: [PATCH 15/18] Add null check and CR feedback

---
 src/compiler/parser.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 13f16f3848acd..e377f988d5533 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -441,14 +441,14 @@ namespace ts {
     /* @internal */
     export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
         const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
-        if (result.jsDocComment) {
+        if (result && result.jsDocComment) {
             // because the jsDocComment was parsed out of the source file, it might
             // not be covered by the fixupParentReferences.
             let parentNode: Node = result.jsDocComment;
             forEachChild(result.jsDocComment, visitNode);
 
             function visitNode(n: Node): void {
-                if (n.parent !== parentNode) {
+                if (n.parent === undefined) {
                     n.parent = parentNode;
 
                     const saveParent = parentNode;

From fc3e040c5167868ed623612e8f33fb3beedf73b1 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders 
Date: Thu, 2 Jun 2016 12:57:24 -0700
Subject: [PATCH 16/18] Revert "Merge pull request #7235 from
 weswigham/narrow-all-types"

This reverts commit ef0f6c8fe4f94a7e294cfe42d7025c9dca6535d5, reversing
changes made to 9f087cb62ade7a879e23c229df752fc8f87d679c.
---
 src/compiler/checker.ts                       |  2 +-
 src/compiler/types.ts                         |  8 +-
 .../typeGuardNarrowsPrimitiveIntersection.js  | 38 ----------
 ...eGuardNarrowsPrimitiveIntersection.symbols | 70 -----------------
 ...ypeGuardNarrowsPrimitiveIntersection.types | 76 -------------------
 .../typeGuardNarrowsToLiteralType.js          | 21 -----
 .../typeGuardNarrowsToLiteralType.symbols     | 32 --------
 .../typeGuardNarrowsToLiteralType.types       | 35 ---------
 .../typeGuardNarrowsToLiteralTypeUnion.js     | 21 -----
 ...typeGuardNarrowsToLiteralTypeUnion.symbols | 32 --------
 .../typeGuardNarrowsToLiteralTypeUnion.types  | 35 ---------
 .../typeGuardNarrowsPrimitiveIntersection.ts  | 21 -----
 .../typeGuardNarrowsToLiteralType.ts          | 10 ---
 .../typeGuardNarrowsToLiteralTypeUnion.ts     | 10 ---
 14 files changed, 2 insertions(+), 409 deletions(-)
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.types
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ef11dc904ea19..06ef246340252 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7662,7 +7662,7 @@ namespace ts {
 
         function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
             let key: string;
-            if (!reference.flowNode || assumeInitialized && (declaredType.flags & TypeFlags.NotNarrowable)) {
+            if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
                 return declaredType;
             }
             const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 026ee5da9604e..c7e14a1003108 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2218,13 +2218,7 @@ namespace ts {
         ObjectType = Class | Interface | Reference | Tuple | Anonymous,
         UnionOrIntersection = Union | Intersection,
         StructuredType = ObjectType | Union | Intersection,
-
-        // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow.
-        // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep
-        // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value)
-        // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing
-        // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules)
-        NotNarrowable = Void,
+        Narrowable = Any | ObjectType | Union | TypeParameter,
         /* @internal */
         RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
         /* @internal */
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
deleted file mode 100644
index a4cba6f4ab5c1..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
+++ /dev/null
@@ -1,38 +0,0 @@
-//// [typeGuardNarrowsPrimitiveIntersection.ts]
-type Tag = {__tag: any};
-declare function isNonBlank(value: string) : value is (string & Tag);
-declare function doThis(value: string & Tag): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isNonBlank(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-const enum Tag2 {}
-declare function isNonBlank2(value: string) : value is (string & Tag2);
-declare function doThis2(value: string & Tag2): void;
-declare function doThat2(value: string) : void;
-if (isNonBlank2(value)) {
-    doThis2(value);
-} else {
-    doThat2(value);
-}
-
-
-//// [typeGuardNarrowsPrimitiveIntersection.js]
-var value;
-if (isNonBlank(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
-if (isNonBlank2(value)) {
-    doThis2(value);
-}
-else {
-    doThat2(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
deleted file mode 100644
index da507fb94c5dc..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
+++ /dev/null
@@ -1,70 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
-type Tag = {__tag: any};
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
->__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12))
-
-declare function isNonBlank(value: string) : value is (string & Tag);
->isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
-
-declare function doThis(value: string & Tag): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24))
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-if (isNonBlank(value)) {
->isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-}
-
-
-const enum Tag2 {}
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function isNonBlank2(value: string) : value is (string & Tag2);
->isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function doThis2(value: string & Tag2): void;
->doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25))
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function doThat2(value: string) : void;
->doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25))
-
-if (isNonBlank2(value)) {
->isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-    doThis2(value);
->doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-} else {
-    doThat2(value);
->doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-}
-
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
deleted file mode 100644
index 478363669b461..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
+++ /dev/null
@@ -1,76 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
-type Tag = {__tag: any};
->Tag : { __tag: any; }
->__tag : any
-
-declare function isNonBlank(value: string) : value is (string & Tag);
->isNonBlank : (value: string) => value is string & { __tag: any; }
->value : string
->value : any
->Tag : { __tag: any; }
-
-declare function doThis(value: string & Tag): void;
->doThis : (value: string & { __tag: any; }) => void
->value : string & { __tag: any; }
->Tag : { __tag: any; }
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isNonBlank(value)) {
->isNonBlank(value) : boolean
->isNonBlank : (value: string) => value is string & { __tag: any; }
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: string & { __tag: any; }) => void
->value : string & { __tag: any; }
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
-const enum Tag2 {}
->Tag2 : Tag2
-
-declare function isNonBlank2(value: string) : value is (string & Tag2);
->isNonBlank2 : (value: string) => value is string & Tag2
->value : string
->value : any
->Tag2 : Tag2
-
-declare function doThis2(value: string & Tag2): void;
->doThis2 : (value: string & Tag2) => void
->value : string & Tag2
->Tag2 : Tag2
-
-declare function doThat2(value: string) : void;
->doThat2 : (value: string) => void
->value : string
-
-if (isNonBlank2(value)) {
->isNonBlank2(value) : boolean
->isNonBlank2 : (value: string) => value is string & Tag2
->value : string
-
-    doThis2(value);
->doThis2(value) : void
->doThis2 : (value: string & Tag2) => void
->value : string & Tag2
-
-} else {
-    doThat2(value);
->doThat2(value) : void
->doThat2 : (value: string) => void
->value : string
-}
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
deleted file mode 100644
index fdd2ee3fb7f75..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
+++ /dev/null
@@ -1,21 +0,0 @@
-//// [typeGuardNarrowsToLiteralType.ts]
-declare function isFoo(value: string) : value is "foo";
-declare function doThis(value: "foo"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-
-//// [typeGuardNarrowsToLiteralType.js]
-var value;
-if (isFoo(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
deleted file mode 100644
index 0d0ddc5f00dc1..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
+++ /dev/null
@@ -1,32 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
-declare function isFoo(value: string) : value is "foo";
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
-
-declare function doThis(value: "foo"): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-if (isFoo(value)) {
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
deleted file mode 100644
index 9835206deb9f4..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
+++ /dev/null
@@ -1,35 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
-declare function isFoo(value: string) : value is "foo";
->isFoo : (value: string) => value is "foo"
->value : string
->value : any
-
-declare function doThis(value: "foo"): void;
->doThis : (value: "foo") => void
->value : "foo"
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isFoo(value)) {
->isFoo(value) : boolean
->isFoo : (value: string) => value is "foo"
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: "foo") => void
->value : "foo"
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
deleted file mode 100644
index 715b362c8e0c2..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
+++ /dev/null
@@ -1,21 +0,0 @@
-//// [typeGuardNarrowsToLiteralTypeUnion.ts]
-declare function isFoo(value: string) : value is ("foo" | "bar");
-declare function doThis(value: "foo" | "bar"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-
-//// [typeGuardNarrowsToLiteralTypeUnion.js]
-var value;
-if (isFoo(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
deleted file mode 100644
index 356fa06a06c4a..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
+++ /dev/null
@@ -1,32 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
-declare function isFoo(value: string) : value is ("foo" | "bar");
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
-
-declare function doThis(value: "foo" | "bar"): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-if (isFoo(value)) {
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
deleted file mode 100644
index 321a8861d55f5..0000000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
+++ /dev/null
@@ -1,35 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
-declare function isFoo(value: string) : value is ("foo" | "bar");
->isFoo : (value: string) => value is "foo" | "bar"
->value : string
->value : any
-
-declare function doThis(value: "foo" | "bar"): void;
->doThis : (value: "foo" | "bar") => void
->value : "foo" | "bar"
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isFoo(value)) {
->isFoo(value) : boolean
->isFoo : (value: string) => value is "foo" | "bar"
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: "foo" | "bar") => void
->value : "foo" | "bar"
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
deleted file mode 100644
index 376a78275c83b..0000000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-type Tag = {__tag: any};
-declare function isNonBlank(value: string) : value is (string & Tag);
-declare function doThis(value: string & Tag): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isNonBlank(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-const enum Tag2 {}
-declare function isNonBlank2(value: string) : value is (string & Tag2);
-declare function doThis2(value: string & Tag2): void;
-declare function doThat2(value: string) : void;
-if (isNonBlank2(value)) {
-    doThis2(value);
-} else {
-    doThat2(value);
-}
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
deleted file mode 100644
index 3b7d5bba21ab7..0000000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare function isFoo(value: string) : value is "foo";
-declare function doThis(value: "foo"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
deleted file mode 100644
index 8f4726160f4b2..0000000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare function isFoo(value: string) : value is ("foo" | "bar");
-declare function doThis(value: "foo" | "bar"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-

From e2a1a78dd30ea9f025b3990b73d68ecbb3eec160 Mon Sep 17 00:00:00 2001
From: zhengbli 
Date: Thu, 2 Jun 2016 13:26:15 -0700
Subject: [PATCH 17/18] reuse the fixupParentReferences function

---
 src/compiler/parser.ts | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index e377f988d5533..b24dd85f2f0dd 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -444,19 +444,7 @@ namespace ts {
         if (result && result.jsDocComment) {
             // because the jsDocComment was parsed out of the source file, it might
             // not be covered by the fixupParentReferences.
-            let parentNode: Node = result.jsDocComment;
-            forEachChild(result.jsDocComment, visitNode);
-
-            function visitNode(n: Node): void {
-                if (n.parent === undefined) {
-                    n.parent = parentNode;
-
-                    const saveParent = parentNode;
-                    parentNode = n;
-                    forEachChild(n, visitNode);
-                    parentNode = saveParent;
-                }
-            }
+            Parser.fixupParentReferences(result.jsDocComment);
         }
 
         return result;
@@ -671,14 +659,14 @@ namespace ts {
             return node;
         }
 
-        export function fixupParentReferences(sourceFile: Node) {
+        export function fixupParentReferences(rootNode: Node) {
             // normally parent references are set during binding. However, for clients that only need
             // a syntax tree, and no semantic features, then the binding process is an unnecessary
             // overhead.  This functions allows us to set all the parents, without all the expense of
             // binding.
 
-            let parent: Node = sourceFile;
-            forEachChild(sourceFile, visitNode);
+            let parent: Node = rootNode;
+            forEachChild(rootNode, visitNode);
             return;
 
             function visitNode(n: Node): void {

From 8012ff1f5d6a22dc6a27774cd6c0f2bb56e4a591 Mon Sep 17 00:00:00 2001
From: Kanchalai Tanglertsampan 
Date: Fri, 3 Jun 2016 10:03:12 -0700
Subject: [PATCH 18/18] Fix up error from merging with master

---
 tests/cases/unittests/tsserverProjectSystem.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts
index c69821ced5f98..60ec635ba1263 100644
--- a/tests/cases/unittests/tsserverProjectSystem.ts
+++ b/tests/cases/unittests/tsserverProjectSystem.ts
@@ -214,6 +214,9 @@ namespace ts {
         readonly write = (s: string) => notImplemented();
         readonly createDirectory = (s: string) => notImplemented();
         readonly exit = () => notImplemented();
+        readonly getEnvironmentVariable = (s: string) => notImplemented();
+        readonly tryEnableSourceMapsForHost = () => notImplemented();
+
     }
 
     describe("tsserver project system:", () => {