diff --git a/Jakefile.js b/Jakefile.js index b853cecb8d7ea..5ae4ab6e497d1 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -180,7 +180,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/compiler/binder.ts b/src/compiler/binder.ts index 592bf22a7d686..c72168aec9244 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1163,6 +1163,8 @@ namespace ts { case SyntaxKind.ModuleBlock: return ContainerFlags.IsControlFlowContainer; + case SyntaxKind.PropertyDeclaration: + return (node).initializer ? ContainerFlags.IsControlFlowContainer : 0; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cb24f8cefd488..5cf984e74515b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -108,16 +108,16 @@ 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 unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); const neverType = createIntrinsicType(TypeFlags.Never, "never"); @@ -3403,7 +3403,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; } @@ -5009,6 +5009,7 @@ namespace ts { containsAny?: boolean; containsUndefined?: boolean; containsNull?: boolean; + containsNonWideningType?: boolean; } function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) { @@ -5019,6 +5020,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); @@ -5079,8 +5081,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) { @@ -5880,7 +5882,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; @@ -6970,7 +6972,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))); } @@ -7017,7 +7019,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); @@ -8309,7 +8311,7 @@ namespace ts { const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); - return baseConstructorType === nullType; + return baseConstructorType === nullWideningType; } function checkThisExpression(node: Node): Type { @@ -9200,7 +9202,7 @@ namespace ts { } } } - return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType); + return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType); } function isNumericName(name: DeclarationName): boolean { @@ -12004,7 +12006,7 @@ namespace ts { function checkVoidExpression(node: VoidExpression): Type { checkExpression(node.expression); - return undefinedType; + return undefinedWideningType; } function checkAwaitExpression(node: AwaitExpression): Type { @@ -12411,7 +12413,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: @@ -12678,7 +12680,7 @@ namespace ts { case SyntaxKind.SuperKeyword: return checkSuperExpression(node); case SyntaxKind.NullKeyword: - return nullType; + return nullWideningType; case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: return booleanType; @@ -12736,7 +12738,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: @@ -17728,7 +17730,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/parser.ts b/src/compiler/parser.ts index 0edd67341e65c..60ea9f7171df3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -443,7 +443,14 @@ 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 && result.jsDocComment) { + // because the jsDocComment was parsed out of the source file, it might + // not be covered by the fixupParentReferences. + Parser.fixupParentReferences(result.jsDocComment); + } + + return result; } /* @internal */ @@ -655,14 +662,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 { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6865bda2f42aa..917051f349f98 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2264,7 +2264,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 */ @@ -2287,9 +2287,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; 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/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/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/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/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
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([]);
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("}"));
diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts
new file mode 100644
index 0000000000000..60ec635ba1263
--- /dev/null
+++ b/tests/cases/unittests/tsserverProjectSystem.ts
@@ -0,0 +1,297 @@
+/// 
+
+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();
+        readonly getEnvironmentVariable = (s: string) => notImplemented();
+        readonly tryEnableSourceMapsForHost = () => 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