You can also spread an object into another object. A common use case is to simply add a property to an object without mutating the original:
const point2D = {x: 1, y: 2};
/** Create a new object by using all the point2D props along with z */ const point3D = {...point2D, z: 3}; Another common use case is a simple shallow extend: const foo = {a: 1, b: 2}; const bar = {c: 1, d: 2}; /** Merge foo and bar */ const fooBar = {...foo, ...bar};
Summary
apply is something that you would inevitably do in JavaScript, so it's good to have a better syntax where you don't have that ugly null for the this argument. Also having adedicated syntax for moving arrays out of (destructuring) or into (assignment) other arrays provides neat syntax for when you are doing array processing on partial arrays.
for...of
A common error experienced by beginning JavaScript developers is that for...in for an array does not iterate over the array items. Instead it iterates over the keys of the object passed in. This is demonstrated in the below example. Here you would expect 9,2,5 but you get the indexes 0,1,2 :
var someArray = [9, 2, 5];
for (var item in someArray) { console.log(item); // 0,1,2 }
This is one of the reasons why for...of exists in TypeScript (and ES6). The following iterates over the array correctly logging out the members as expected:
var someArray = [9, 2, 5];
for (var item of someArray) { console.log(item); // 9,2,5 }
Similarly TypeScript has no trouble going through a string character by character using for...of :
var hello = "is it me you're looking for?";
for (var char of hello) {
console.log(char); // is it me you're looking for? }
JS Generation
For pre ES6 targets TypeScript will generate the standard for (var i = 0; i < list.length; i++) kind of loop. For example here's what gets generated for our previous example: for...ofvar someArray = [9, 2, 5];
for (var item of someArray) { console.log(item);
}
// becomes //
for (var _i = 0; _i < someArray.length; _i++) { var item = someArray[_i];
console.log(item); }
You can see that using for...of makes intent clearer and also decreases the amount of code you have to write (and variable names you need to come up with).
Limitations
If you are not targeting ES6 or above, the generated code assumes the property length exists on the object and that the object can be indexed via numbers e.g. obj[2] . So it is only supported on string and array for these legacy JS engines.
If TypeScript can see that you are not using an array or a string it will give you a clear error "is not an array type or a string type";
let articleParagraphs = document.querySelectorAll("article > p"); // Error: Nodelist is not an array type or a string type
for (let paragraph of articleParagraphs) { paragraph.classList.add("read"); }
Use for...of only for stuff that you know to be an array or a string. Note that this limitation might be removed in a future version of TypeScript.
Summary
You would be surprised at how many times you will be iterating over the elements of an array. The next time you find yourself doing that, give for...of a go. You might just make the next person who reviews your code happy.
Iterators
Iterator itself is not a TypeScript or ES6 feature, Iterator is a Behavioral Design Pattern common for Object oriented programming languages. It is, generally, an object which implements the following interface:
interface Iterator<T> {
next(value?: any): IteratorResult<T>; return?(value?: any): IteratorResult<T>; throw?(e?: any): IteratorResult<T>; }
This interface allows to retrieve a value from some collection or sequence which belongs to the object.
The IteratorResult is simply a value + done pair:
interface IteratorResult<T> { done: boolean; value: T; } Imagine that there's an object of some frame, which includes the list of components of which this frame consists. With Iterator interface it is possible to retrieve components from this frame object like below: Iterators
class Component {
constructor (public name: string) {} }
class Frame implements Iterator<Component> { private pointer = 0;
constructor(public name: string, public components: Component[]) {} public next(): IteratorResult<Component> {
if (this.pointer < this.components.length) { return {
done: false,
value: this.components[this.pointer++] } } else { return { done: true } } } }
let frame = new Frame("Door", [new Component("top"), new Component("bottom"), new Comp onent("left"), new Component("right")]);
let iteratorResult1 = frame.next(); //{ done: false, value: Component { name: 'top' } } let iteratorResult2 = frame.next(); //{ done: false, value: Component { name: 'bottom' } } let iteratorResult3 = frame.next(); //{ done: false, value: Component { name: 'left' } } let iteratorResult4 = frame.next(); //{ done: false, value: Component { name: 'right' } } let iteratorResult5 = frame.next(); //{ done: true } //It is possible to access the value of iterator result via the value property: let component = iteratorResult1.value; //Component { name: 'top' } Again. Iterator itself is not a TypeScript feature, this code could work without implementing Iterator and IteratorResult interfaces explicitly. However it is very helpful to use these common ES6 interfaces for code consistency.
Ok, Nice, but could be more helpful. ES6 defines the iterable protocol which includes [Symbol.iterator] symbol if Iterable interface implemented:
//...
class Frame implements Iterable<Component> {
constructor(public name: string, public components: Component[]) {} [Symbol.iterator]() {
let pointer = 0;
let components = this.components; return { next(): IteratorResult<Component> { if (pointer < components.length) { return { done: false, value: components[pointer++] } } else { return { done: true, value: null } } } } } }
let frame = new Frame("Door", [new Component("top"), new Component("bottom"), new Comp onent("left"), new Component("right")]);
for (let cmp of frame) { console.log(cmp); }
Unfortunately frame.next() won't work with this pattern and it also looks a bit clunky. IterableIterator interface to the rescue!
//...
class Frame implements IterableIterator<Component> { private pointer = 0;
constructor(public name: string, public components: Component[]) {} public next(): IteratorResult<Component> {
if (this.pointer < this.components.length) { return {
done: false,
value: this.components[this.pointer++] } } else { return { done: true, value: null } } } [Symbol.iterator](): IterableIterator<Component> { return this; } } //...
Both frame.next() and for cycle now work fine with IterableIterator interface.
Iterator does not have to iterate a finite value. The typical example is a Fibonacci sequence:
class Fib implements IterableIterator<number> { protected fn1 = 0;
protected fn2 = 1;
constructor(protected maxValue?: number) {} public next(): IteratorResult<number> { var current = this.fn1;
this.fn1 = this.fn2;
this.fn2 = current + this.fn1;
if (this.maxValue != null && current >= this.maxValue) { return { done: true, value: null } } return { done: false, value: current } } [Symbol.iterator](): IterableIterator<number> { return this; } }
let fib = new Fib();
fib.next() //{ done: false, value: 0 } fib.next() //{ done: false, value: 1 } fib.next() //{ done: false, value: 1 } fib.next() //{ done: false, value: 2 } fib.next() //{ done: false, value: 3 } fib.next() //{ done: false, value: 5 }
let fibMax50 = new Fib(50);
console.log(Array.from(fibMax50)); // [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
let fibMax21 = new Fib(21);
for(let num of fibMax21) {
console.log(num); //Prints fibonacci sequence 0 to 21 }