Empty spaces are ignored by the InnerText property

Don't use presentation as data. Store the current content as a separate string, don't pull it from the DOM. This way you're not dependent on how the browser stores the element's text content.

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", "y", "o", "u", "?"]
 
function typeText() {
    var i = 0;
    var paragText = "";
    var interval = setInterval(function () {
        var parag = document.getElementById("theParagraph");
        paragText += text[i];
        parag.innerText = paragText;
        i++;
        if (text.length == i)
            clearInterval(interval);
    }, 200)
}
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

As a side note, the same thing could be made a lot simpler:

var text = "Hello how are you?";

function typeText() {
    var i = 0;
    var interval = setInterval(function () {
        var parag = document.getElementById("theParagraph");
        parag.innerText = text.substr(0, i);
        if (text.length == i)
            clearInterval(interval);
        i++;
    }, 200)
}
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

What about using textContent?

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ","y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.textContent;
    parag.textContent = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

You can also use innerHTML:

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.innerHTML;
    parag.innerHTML = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

innerText was introduced by IE and, as we all know, nothing good comes from IE. Joking apart, this is a good explanation about it: "The poor, misunderstood innerText".


You need to introduce the space using &nbsp; and use innerHTML instead of innerText

var paragOldText = parag.innerHTML;
parag.innerHTML = paragOldText + ( text[i].trim().length ? text[i] : "&nbsp;" ) ;

Edit

&nbsp; isn't required with innerHTML

var paragOldText = parag.innerHTML;
parag.innerHTML = paragOldText + text[i] ;

Demo

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", "y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.innerHTML;
    parag.innerHTML = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

The other answers address the issues with your code, but I'd like to address issues with your whole plan.

  • Do you really want to be defining an array of characters? Long sentences are going to be hell. And what if you want variable text? Use this instead:

    var input = "Hello how are you?";
    var text = input.split(""); // split into array of characters
    
  • Speaking of longer sentences, your "typewriter" will fill out the current line, realise it doesn't have room, and then bump the last word down to the next line to finish it. This is not a good look! You can get around this with a clever trick:

    <p><span id="visible_text">Hello how a</span><span id="remaining_text">re you?</span></p>
    <style>#remaining_text {visibility:hidden}</style>
    

    Not only will this handle word wrapping very nicely, it will also "reserve" the necessary space ahead of time so that you don't end up with it pushing the content below the typewriter further down the page as new lines arise.

    You can easily achieve this effect by counting which character position you are at, then splitting the input string into two pieces at that offset. Put the first piece in the first <span>, the rest in the second, and you're golden.

Source: I use this technique in my "RPG cutscene"-style code. Actually a more advanced version, as mine also supports HTML rather than just plain text!