From 5f3f2d302fbbb5fd95c81534944553221914a342 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:47:37 -0700 Subject: [PATCH 1/2] 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 2/2] 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