Strings are values that never change
A TypeScript string is a sequence of characters, and each character has an index that starts at 0. You read a character with square brackets and ask for the size with .length:
let str: string = "Hello";
console.log(str[0]); // H
console.log(str[str.length - 1]); // o
The key property of strings is that they are immutable. You can read a character, but you can't replace it. The compiler rejects str[0] = "J", and the string would not change even if the assignment ran. To get a different string, you build a new one and store it in the variable:
let myStr: string = "Bob";
myStr = "Job";
myStr = "J" + myStr.slice(1, 3);
console.log(myStr); // Job
The variable is what changes: it now refers to a new string. This is why most of this lesson is about methods that return a new string or an array, instead of editing in place.
Joining with + and +=
The + operator joins two strings into a new one. The += operator joins and stores the result back in the variable, so it is the natural way to assemble a string piece by piece:
let greet: string = "Welcome";
greet += " to the course, ";
greet += "Ana";
console.log(greet); // Welcome to the course, Ana
Each += doesn't append to the old string. It creates a new string from the old one plus the right side, and the variable then points to it.
Slicing with slice
slice(startIndex, endIndex) returns the part of the string from startIndex up to, but not including, endIndex. If you leave out the end, it goes to the end of the string:
let word: string = "JavaScript";
console.log(word.slice(2, 6)); // vaSc
console.log(word.slice(4)); // Script
The "up to but not including" rule is easy to forget. The length of the result is endIndex - startIndex, so slice(2, 6) returns 4 characters. An end index past the length is not an error: "abc".slice(1, 100) is "bc". A negative index counts from the end, so slice(-2) gives the last two characters.
Splitting into an array
split(separator) cuts a string at every occurrence of the separator and returns an array of the pieces. The original string is untouched:
let sentence: string = "I am happy!";
let parts: string[] = sentence.split(" ");
console.log(parts); // [ 'I', 'am', 'happy!' ]
Splitting on the empty string "" cuts between every pair of characters, which gives an array with one one-character string per element. Two edge cases are worth knowing: "a,b,,c".split(",") keeps the empty piece, giving ["a", "b", "", "c"], and "".split(" ") gives [""], an array with one empty string, not an empty array.
Editing characters through an array
Changing one character of a string means building a whole new string, which costs time proportional to its length. When you need many changes, it is cheaper to convert the string to an array of characters, change the array, and join it back. Arrays are mutable, and assigning to an element is a single step:
let name1: string = "Andy";
let chars: string[] = name1.split("");
chars[0] = "a";
chars[2] = "D";
name1 = chars.join("");
console.log(name1); // anDy
join(separator) is the opposite of split. It glues the elements together with the separator between them, and join("") uses nothing at all. Leaving the argument out is a trap: join() uses a comma, so ["a", "b"].join() gives "a,b".
One caution: split("") cuts between 16-bit units, not between visible characters. For letters and digits that is the same thing, but an emoji such as "😀" is cut into two broken halves.
Looping over the characters
You can loop with for...of, which gives each character, or with an index and length:
const text: string = "Andy";
for (const c of text) {
console.log(c);
}
for (let i = 0; i < text.length; i++) {
console.log(text[i]);
}
Both print A, n, d and y on separate lines and take time proportional to the length of the string.
The lesson's exercise, step by step
The exercise turns "Java" into "TypeScript" using the tools above.
- Create
lang, annotated asstring, holding"Java". It is declared withletbecause the next step reassigns it. - Append with
+=. Adding"script"makeslanghold"Javascript". The old string isn't edited: a new one is built and stored back. - Take the tail with
slice(4). The first four characters,Java, sit at indexes 0 to 3, so index 4 startsscript. The result goes insubstr. - Build
newLangas"Type"plussubstr. That gives"Typescript", with a lowercase s. - Make a mutable copy. Strings can't be edited, so
charArray, astring[], getsnewLang.split(""). - Capitalize the s. It is the fifth character, so it sits at index 4. Assign
"S"tocharArray[4]. - Join back.
charArray.join("")builds the final string, and assigning it tonewLangmakes it"TypeScript".
let lang: string = "Java";
lang += "script";
let substr: string = lang.slice(4);
let newLang: string = "Type" + substr;
let charArray: string[] = newLang.split("");
charArray[4] = "S";
newLang = charArray.join("");
After the last line, newLang is "TypeScript". If you print lang and substr along the way, you see Javascript and script.
Common mistakes
- Expecting a method to change the string.
str.slice(1)on its own line does nothing useful. Its result is lost unless you store it. - Off-by-one in
slice. The end index is excluded, and the first index is 0. - Using
constand then reassigning.+=assigns, so the variable must be declared withlet. - Forgetting the type on the array.
splitreturnsstring[], and each element is a string, even for digits. - Skipping
join. An array is not a string. Printing or comparing the array instead of the joined result gives something you didn't expect.
Key takeaways
- Strings are immutable: you replace the whole value, not a character.
+=builds a new string and stores it back in the variable.slice(start, end)excludesend, andslice(start)runs to the end.split("")andjoin("")let you edit characters through an array and then rebuild the string.