Categories
JavaScript

Avoiding Shared Mutable State in JavaScript by Deep Copying Data

In JavaScript, like any other program languages, stores things in variables, which can be changed on the fly. This may be a problem because we may change things accidentally that is shared. Having lots of code share the same mutable state is hard to trace. It makes debugging and read the code hard.

For example, if we change the same array in different function as follows:

let arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then the value of arr changes depending on whether foo or bar is called last. If foo is called then arr is [1, 2, 3] . On the other hand, if bar is called then arr is [4, 5, 6] .

This is a problem because as the code gets more complex, then the more function calls there are. If lots of functions are doing things like this, then tracing the value is hard and debugging is confusing.

Also, it’s hard to read how the logic flows as functions are called with these side effects.

In this article, we look at how to do a deep copy of data to prevent mutation of shared state in a program by making deep copies of data. Also, we look at ways to prevent the mutation of data exposed from class methods.

Deep Copy

Nested Spreading

We can use the spread operator in each level of an object to do deep copying manually.

For example, given that we have the following object:

const obj = {
  foo: {
    bar: 1,
    baz: 2
  },
  a: 3
}

We can make a deep copy of it as follows:

const objCopy = {
  foo: {
    ...obj.foo
  },
  a: obj.a
};

As we can see, this is going to be a problem when we have more levels and properties. However, we do get a deep copy of the original object.

Deep Copy Via JSON.stringify and JSON.parse

We can call JSON.stringify to return a string of an object and then call JSON.parse on the stringify to return it to the original form.

This works for all properties and values that are supported by JSON, which means that entities like Symbols and functions are excluded.

For example, we can write:

const obj = {
  foo: {
    bar: 1,
    baz: 2
  },
  a: 3
}

const objCopy = JSON.parse(JSON.stringify(obj));

objCopy will be a deep copy of obj . This is because a string if immutable and JSON.parse returns a new parsed copy of the stringified object.

Copying an Instance of a Class

We can copy an instance of a class easier than with objects. We can write a clone method that returns the instance of the object to do this.

For example, we can write the following code to make one class that inherits from another class:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class Employee extends Person {
  constructor(name, employeeCode) {
    super(name);
    this.employeeCode = employeeCode;
  }

  clone() {
    return new Employee(this.name, this.employeeCode);
  }
}

Then we call clone to create a new duplicate object:

const employee = new Employee('Joe', 1);
const employeeClone = employee.clone();

console.log(employee.__proto__);
console.log(employeeClone.__proto__);

In the clone method of the Employee class, we return a new instance of an Employee .

Then in the first 2 console.log outputs, we see that both employee and employeeClone have the same prototype.

Why Does Copying Help Prevent Mutating Shared State?

Copying prevents the mutation of a shared state because we copied the shared data before we attempt to change it. This is handy because it stops us from accidentally making changes to shared data.

If we don’t make changes to shared data, then tracing it is easy. Then we won’t have to worry about accidentally having different parts of our code mutating the same state.

Copying Exposed Internal Class Data Before Making Changes

Before making changes to internal class data, we should make a copy of it and then return it in a method. This prevents us from changing the class data directly.

For example, if we have the following class:

class NumArray {
  constructor() {
    this._arr = [];
  }

  add(num) {
    this._arr.push(num);
  }

  getParts() {
    return this._arr;
  }

  toString() {
    return this._arr.join('');
  }
}

and we call the methods in various ways by writing the following:

const numArr = new NumArray();
numArr.add(1);
numArr.add(2);
console.log(numArr.toString());
numArr.getParts().length = 0;
console.log(numArr.toString());

We can see that the first console.log has the value '1,2' , and the second one logs an empty string.

This is because we changed the length property of the array exposed by the getParts methods. We set it to 0, so the array is emptied.

To prevent this, we can copy the array before returning it in getParts . We write the following instead:

class NumArray {
  constructor() {
    this._arr = [];
  }

  add(num) {
    this._arr.push(num);
  }

  getParts() {
    return [...this._arr];
  }

  toString() {
    return this._arr.join(',');
  }
}

Then when we make the same calls to the NumArray class’ methods as follows:

const numArr = new NumArray();
numArr.add(1);
numArr.add(2);
console.log(numArr.toString());
numArr.getParts().length = 0;
console.log(numArr.toString());

We get '1,2' in both console.log outputs.

We can do a deep copy of data in various ways. First we can manually copy data by repeatedly using the spread operator in every level.

Also, we can use JSON.stringify and JSON.parse to copy data that can be included in JSON.

For class instances, we can make a method that returns instances of classes by return a new instance of the data with the same data. This preserves the data and the inheritance structure.

Finally, we can prevent data exposed in class methods from being modified by making a copy of it before return it in the method.

Categories
JavaScript

Avoiding Shared Mutable State in JavaScript by Shallow Copying Data

In JavaScript, like any other program languages, stores things in variables, which can be changed on the fly. This may be a problem because we may change things accidentally that is shared. Having lots of code share the same mutable state is hard to trace. It makes debugging and read the code hard.

For example, if we change the same array in different function as follows:

let arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then the value of arr changes depending on whether foo or bar is called last. If foo is called then arr is [1, 2, 3] . On the other hand, if bar is called then arr is [4, 5, 6] .

This is a problem because as the code gets more complex, then the more function calls there are. If lots of functions are doing things like this, then tracing the value is hard and debugging is confusing.

Also, it’s hard to read how the logic flows as functions are called with these side effects.

There’re a few ways to avoid this situation. There’s the const keyword to prevent reassignment. Also, we can copy objects to prevent the original from being modified.

Using Const for Constants

If we want to share constants between different parts of our code, then we can use the const keyword to declare constants. This prevents them from being modified.

For example, if we write:

const arr = [];
const foo = () => {
  arr = [1, 2, 3];
}

const bar = () => {
  arr = [4, 5, 6];
}

Then whenever foo or bar are called, we’ll get an error.

Shallow Copying Data

We can also prevent the original piece of data from being changed while we manipulate data in our functions by making a copy of the original.

There’re 2 ways to copy data. One is to do a shallow copy, where we copy the top-level entries of an object or array.

If we have nested arrays or objects, then we have to do a deep copy, where we copy all the levels of an object or array.

In this article, we’ll look at how to shallow copy data.

To make a shallow copy, we can use the spread operator. It works for both objects an arrays.

For example, we can write the following for objects:

let obj = {
  a: 1,
  b: 2,
  c: 3
};
let objCopy= {
  ...obj
};

And we can write the following for arrays:

let arr = [1, 2, 3];
let arrCopy = [...arr];

There’re a few limitations with using the spread operator. First, the prototype isn’t copied, so if we have things that inherit from some prototype like we have in the following code:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
obj.a = 2;

let objCopy = {
  ...obj
};

console.log(obj.__proto__);
console.log(objCopy.__proto__);

We see that the first console.log output is completely different from the second. obj ‘s prorotype is {foo: 1} which we explicitly set. However, objCopy ‘s prototype is Object.prototype , which is completely different.

Special objects like regular expressions also special internal slots that aren’t copied.

Also, we can see from the log output above that inherited values aren’t copied with the spread operator.

In addition, only enumerable properties are copied. This means for instance, if we copy an array to an object with the spread operator as follows:

let arr = [1, 2];
let obj = {
  ...arr
};

console.log(arr);
console.log(obj);

We missing the length property from the second console.log ‘s output.

Finally, getters, setters and property descriptors also aren’t copied over. For example, if we write:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
Object.defineProperty(obj, 'b', {
  value: 2,
  writable: false,
  enumerable: true
})

console.log(Object.getOwnPropertyDescriptors(obj))
console.log(Object.getOwnPropertyDescriptors(objCopy))

Then we that the property descriptors of obj and objCopy are different.

We can get around some of these issues. We can copy the prototype of the original object into the new object as follows:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
obj.a = 2;
let objCopy = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
console.log(obj.__proto__);
console.log(objCopy.__proto__);

Then we get that both obj and objCopy have the same prototype in the console.log statements above.

We can copy over the value along with other property descriptors by writing the following code:

let obj = Object.create({
  foo: 1
})
obj.a = 1;
Object.defineProperty(obj, 'b', {
  value: 2,
  writable: false,
  enumerable: true
})

let objCopy = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj))

console.log(Object.getOwnPropertyDescriptors(obj))
console.log(Object.getOwnPropertyDescriptors(objCopy))

The property descriptor object includes the value of a property, so we can define all the properties with the defineProperties method and pass in the property descriptors with the getOwnPropertyDescriptors called with obj passed in to get obj ‘s property descriptors.

We should see that property b has writable set to false in the property descriptor of both obj and objCopy .

Preventing shared mutable state is a problem in JavaScript. We want to avoid this to prevent mutating shared data, which makes tracing code and debugging tough.

Copying objects in JavaScript precisely is tricky. The spread operator doesn’t do a thorough copy of an object. The property descriptors, getters and settings, and prototype aren’t copied over. Most of these issues can be solved by copying them over manually as we did with the object’s prototype and property descriptors.

For constants, we use const to prevent accidental reassignment.

Categories
JavaScript

More JavaScript Array Tips

Replacing and mapping entries

JavaScript, like any other programming language, has many handy tricks that let us write our programs more easily. In this article, we will look at how to do different things that involve arrays, like replacing specific values from an array and mapping array entries from one value to another.

Replacing Specific Value From an Array

There are a few ways to replace specific values from an array. We can use the indexOf method to get the first occurrence of an array and then use the index to assign a new value to the entry in that array index. For example, we can use the indexOf method like the following code:

const arr = ['apple', 'orange', 'grape', 'banana'];
const index = arr.indexOf('orange');
arr[index] = 'chicken';
console.log(arr);

The indexOf is called on arr and takes in any object that we want to get the first index of in the array. It works best with primitive values since it doesn’t do deep checks for objects, so it only works by checking the references for objects. In the third line, we reassigned the value of the index that is assigned by the indexOf method, which should be 1. Then, we assigned it the new value 'chicken'. Then, we should get the following output from the console.log statement on the last line:

["apple", "chicken", "grape", "banana"]

Note that we can use const to declare arr since we aren’t assigning any new value to any property of arr so it will work without errors.

We can also use the splice method to replace one or more values of an array. This method lets us removing, replace an existing element or adding new elements in place. The argument of the splice method is the starting index start of which to start changing the array. It can be positive or negative. If it’s negative, then it’ll start changing the array from the end and move towards the start of the array. The end index is -1, the second is -2 and so on. The second argument of the splice method is the deleteCount, which is an optional argument that lets us specify how many items to delete starting from the start parameter in the first element. Subsequent arguments are the items that we want to insert into an array. This can go on for as long as we want. Only the first argument is required.

We can use the splice method to first remove the entry that we want to replace by getting the index of the item that we want to replace, and then we can use the splice method to insert a new entry in its place like in the following code:

const arr = ['apple', 'orange', 'grape', 'banana'];
const index = arr.indexOf('orange');
arr.splice(index, 1);
arr.splice(index, 0, 'chicken');
console.log(arr);

If we run the code above, we should get the same output as we did before:

["apple", "chicken", "grape", "banana"]

The first two lines are the same as the first example. Then, we called the splice method the first time to remove the original entry in the index. The first argument is the index of the array we want to remove, and the second specifies that we only remove one entry, which is the entry specified by the index. Then we call splice again to insert the new entry in its place. We pass in index again in the second splice call since we want to insert the new element in the same place as the original. The second argument is zero since we don’t want to remove any entry. Then, we pass in 'chicken' in the third argument so that we get 'chicken' in the same position that 'orange' was in.

Map Array Entries From One Value to Another

If we want to map each entry of an array to a new value, we can do it in a few ways. We can either use the map method or the Array.from method do to this. The map method is an array instance method that takes a callback function that has up to three parameters. The first parameter is the value of the array that’s being processed by the map method. This is a required parameter. The second parameter is an optional parameter, which is the index of the array entry that’s being processed in the array. The third argument is the array of which the map method is being called on. The callback returns the value that we want the new value to have.

For example, if we want to get a field of each array entry into a new array, we can write the following code:

const arr = [{
    food: 'apple',
    color: 'red'
  },
  {
    food: 'orange',
    color: 'orange'
  },
  {
    food: 'grape',
    color: 'purple'
  },
  {
    food: 'banana',
    color: 'yellow'
  }
];
const foodColors = arr.map(({
  color
}) => color);
console.log(foodColors);

In the code above, we used the map method to get the value of the color field and put it in a new array. In the map method, we passed in a callback function with the first parameter, with the objects in the arr array destructured into color variable, and the color variable is retrieved within the parameter then we returned it. This is will get us the value of the color field of each entry into the new foodColors array.

Alternatively, we can use the Array.from method to do the same thing. The Array.from method creates a new shallow copied array instance from an array-like or other iterable objects. The first argument that it accepts is an array or other array-like or iterable objects like NodeList, arguments , strings, TypedArrays like Uinit8Array, Map, other Sets, and any other object that have a Symbol.iterator method. The second argument is an optional callback argument function we can use to map each entry from one value to another. The callback function takes two parameters, which is the entry that’s being processed by the from method. The from method will iterate through the whole iterable object or array to map each entry to a new value. The second parameter is the index of the array or iterable that’s being processed. It returns a new array with the new entry

For example, we can replace the map method with the Array.from method with the following code:

const arr = [{
    food: 'apple',
    color: 'red'
  },
  {
    food: 'orange',
    color: 'orange'
  },
  {
    food: 'grape',
    color: 'purple'
  },
  {
    food: 'banana',
    color: 'yellow'
  }
];
const foodColors = Array.from(arr, ({
  color
}) => color);
console.log(foodColors);

In the code above, we used the callback function that we passed into the Array.from method to get the value of the color field and put it in a new array. In the map method, we passed in a callback function with the first parameter, with the objects in the arr array destructured into color variable. The color variable is retrieved within the parameter, then we returned it. This will get us the value of the color field of each entry into the new foodColors array.

There are a few ways to replace specific values from an array. We can use the indexOf method to get the first occurrence of an array and then use the index to assign a new value to the entry in that array index. We can also use the splice method to remove the existing entry given the index, and then add another element to the same position given the same index. If we want to map each entry of an array to a new value, we can do it in a few ways. We can either use the map method or the Array.from method do to this.

Categories
JavaScript

A Guide to the JavaScript window.crypto Object

The window object is a global object that has that provides JavaScript access to the DOM. It also contains a standard library of functions that can we access at any location in our web apps.

In this article, we look at the window.cryoto object.

window.crypto

The window.crypto property returns a Crypto object which is associated with the global object. This object allows web pages to run various cryptographic operations on the browser side. It has one property, which is the subtle property.

The Crypto.subtle property returns a SubtleCrypto object which allows us to do subtle cryptography on the client-side. The SubtleCrypto object has 5 methods for scrambling and unscrambling data. The sign method is for creating digital signatures.

A verify method exists to verify the digital signatures created by the sign method.

The encrypt method is used for encrypting data, and the decrypt method is used for decryption the scrambled data generated by the encrypt method. The digest method is used to create a fixed-length, collision-resistant digest of some data.

We can also use the SubtleCrypto object to generate and derive cryptographic keys with the generateKey and deriveKey methods respectively.

The generateKey method generates a new distinct key value each time we call it, while the deriveKey method derives a key from some initial material. If we provide the same material to 2 separate calls to deriveKey , we will get the same underlying value.

The deriveKey method is useful for deriving the same key for encryption and decryption. We can also use the importKey and exportKey methods to import and export cryptographic keys respectively.

There’s also a wrapKey method that exports the key and then encrypts it with another key.

An unwrapKey method is also provided to decrypt the encrypted key done by the wrapKey method and import the decrypted key.

For example, we can use the sign method to create a digital signature. It takes 3 arguments. The first is the algorithm, which is a string or an object that specifies the signature algorithm to use for creating the digital signature. Possible values are:

  • RSASSA-PKCS1-v1_5 — pass in the string “RSASSA-PKCS1-v1_5” or an object of the form { “name”: “RSASSA-PKCS1-v1_5” }
  • RSA-PSS — pass an RsaPssParams object. An RsaPssParams object has the name property which should be RSA-PSS , and saltLength which is the length of the random salt to use measured in bytes. The maximum value of saltLength is Math.ceil((keySizeInBits - 1)/8) - digestSizeInBytes - 2
  • ECDSA — pass an EcdsaParams object. An EcdsaParams object has the name property, which should be the string 'ECDSA' and the hash property, which is string that can have the possible values of SHA-256, SHA-384 or SHA-512
  • HMAC — pass in the string “HMAC” or an object of the form { “name”: “HMAC” }

The second argument is the key which is a CryptoKey object that has the private key to be used for creating the signature. The third argument is the data which is an ArrayBuffer or ArrayBufferView object that has the data to be signed.

The sign method returns a promise that’s fulfilled with an ArrayBuffer object that has the signature.

Likewise, the verify method takes in the same first algorithm , key , and data argument as the sign method as the first, second and fourth arguments. The signature generated from the sign method is the third argument. It returns a promise that fulfills with the value true if the signature is valid and false otherwise.

To use the sign and verify methods, we can write something like the following code:

const enc = new TextEncoder();
const encodedMessage = enc.encode('hello');
const keyPair = window.crypto.subtle.generateKey({
    name: "RSASSA-PKCS1-v1_5",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["sign", "verify"]
);

(async () => {
  const {
    privateKey,
    publicKey
  } = await keyPair;
  const signature = await window.crypto.subtle.sign(
    "RSASSA-PKCS1-v1_5",
    privateKey,
    encodedMessage
  );
  const signatureValid = await window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", publicKey, signature, encodedMessage);
  console.log(signatureValid);
})()

We first generate the key pair with the generateKey since we’re using the asymmetric RSA algorithm which has a private and public key. The generateKey method takes the algorithm as the first argument, where the possible values are:

  • To use RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP we pass an [RsaHashedKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams) object.
  • To use ECDSA or ECDH we pass an [EcKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/EcKeyGenParams) object.
  • To use HMAC we pass an [HmacKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/HmacKeyGenParams) object.
  • To use AES-CTR, AES-CBC, AES-GCM, or AES-KW we pass an [AesKeyGenParams](https://developer.mozilla.org/en-US/docs/Web/API/AesKeyGenParams) object.

The second argument is the boolean extractable property which indicates whether it’s possible to export a key using the SubtleCrypto.exportKey() or SubtleCrypto.wrapKey() methods. The third argument is the keyUsages array which indicates which methods can be used with the keys generated:

  • encrypt
  • decrypt
  • sign
  • verify
  • deriveKey
  • deriveBits
  • wrapKey
  • unwrapKey

Then we call the sign method with the algorithm name, private key, and the encoded message we generated from the TextEncoder . This generates the signature. Then we call the verify method with the algorithm name, private key, the generated signature fulfilled from the sign method and the same encoded message. If we run the code above, we should get console.log logging true .

The encrypt method takes 3 arguments. The first is the algorithm , which is an object with the following possible values:

The second argument is the CryptoKey object which we use to do the encryption. The third argument is a BufferSource object that has the data to be encrypted, also known as plain text. It returns a promise that’s resolved with an ArrayBuffer object containing the encrypted plain text.

Likewise, the decrypt method takes the same first 2 arguments as the encrypt method, except that the third argument is a BufferSource that has the data to be decrypted. It returns a promise that’s resolved with the ArrayBuffer object which has the plain text.

For example, we can use the encrypt and dercrypt methods like in the following code:

const enc = new TextEncoder();
const dec = new TextDecoder();
const keyPair = window.crypto.subtle.generateKey({
    name: "RSA-OAEP",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  },
  true,
  ["encrypt", "decrypt"]
);
const encodedMessage = enc.encode('hello');
(async () => {
  const {
    privateKey,
    publicKey
  } = await keyPair;
  const encryptedText = await window.crypto.subtle.encrypt({
      name: "RSA-OAEP"
    },
    publicKey,
    encodedMessage
  )
  console.log(encryptedText);

  const decryptedText = await window.crypto.subtle.decrypt({
      name: "RSA-OAEP"
    },
    privateKey,
    encryptedText
  )
  console.log(decryptedText);
  console.log(dec.decode(decryptedText));

})()

In the code above, we first generate a key or key pair with the generateKey method with depending if the encryption algorithm is symmetric or asymmetric like we did with the sign and verify example. Asymmetric cryptographic algorithms have a public and private key like in the example above. RSA is an asymmetric algorithm.

Then we encode the message with the TextEncoder to encode it to a ArrayBuffer object which can used with the encrypt method.

Then we use the encrypt method with the algorithm, the public key, and the ArrayBuffer object with the encoded text passed int to encrypt the data. Then to decrypt the encrypted text, we use the decrypt method with the algorithm object passed in as the first argument, then we pass in the private key from the key pair, then we pass in the encrypted text as the third argument to the decrypt method.

This will get the decrypted data as an ArrayBuffer , which we will decode with the TextDecoder’s decode method with the decrypted ArrayBuffer to get back the original text. This means that the last console.log statement to get us back 'hello' .

The Crypto object also has one method, which is the getRandomValues method. The method will create a strong random value given a typed array. The method takes one argument. It takes a typed array, which is an Int8Array, a Uint8Array, an Int16Array, a Uint16Array, an Int32Array, or a Uint32Array. To improve performance, this method doesn’t generate numbers with a truly random number generator, but rather it uses a pseudo-random number generator to generate the number. The entries of the typed array passed into the argument will be overwritten by the random numbers generated by this method.

We can use the getRandomValues method like in the following example:

let array = new Uint32Array(10);
window.crypto.getRandomValues(array);

for (const num of array) {
  console.log(num);
}

In the code above, we generated a new Uint32Array, which we pass into the getRandomValues method. Then in the for...of loop, we get the generated values which overwrote whatever entries were in the original array. We should see 10 random numbers from the console.log, and each time we run the code above, we should get different results.

With the window.cryoto object, we can encrypt and decrypt data by using well-know cryptographic algorithms on the browser. It supports both symmetric and asymmetric encryption, which let us encrypt data with different algorithms. Also, we can use it to generate digital signatures and verify them. We can also use it to get random numbers with the getRandomValues method.

Categories
TypeScript

Cool New Features Released with TypeScript 3.6

Lots of new features are released with TypeScript 3.6. It includes features for iterables like stricter type checking for generators, more accurate array spreading, allow get and set in declare statements, and more.

In this article, we’ll look at each of them.

Stricter Type Check for Generators

With TypeScript 3.6, the TypeScript compiler has more checks for data types in generators.

Now we have a way to differentiate whether our code yield or return from a generator.

For example, if we have the following generator:

function* bar() {
    yield 1;
    yield 2;
    return "Finished!"
}

let iterator = bar();
let curr = iterator.next();
curr = iterator.next();

if (curr.done) {
    curr.value
}

The TypeScript 3.6 compiler knows automatically that curr.value is a string since we returned a string at the end of the function.

Also, yield isn’t assumed to be of any type when we assign yield to something.

For instance, we have the following code:

function* bar() {
    let x: { foo(): void } = yield;
}

let iterator = bar();
iterator.next();
iterator.next(123);

Now the TypeScript compiler knows that the 123 isn’t assignable to something with the type { foo(): void } , which is the type of x . Whereas in earlier versions, the compiler doesn’t check the type of the code above.

So in TypeScript 3.6 or later, we get the error:

Argument of type '[123]' is not assignable to parameter of type '[] | [{ foo(): void; }]'.

Type '[123]' is not assignable to type '[{ foo(): void; }]'.

Type '123' is not assignable to type '{ foo(): void; }'.

Also, now the type definitions for Generator and Iterator have the return and throw methods present and iterable.

TypeScript 3.6 converts the IteratorResult to the IteratorYieldResult<T> | IteratorReturnResult<TReturn> union type.

It can also infer the value that’s returned from next() from where it’s called.

For example, the following would compile and run since we passed in the right type of value into next() , which is a string:

function* bar() {
    let x: string = yield;
    console.log(x.toUpperCase());
}

let x = bar();
x.next();
x.next('foo');

However, the following would fail to compile because of type mismatch between the argument and the type of x :

function* bar() {
    let x: string = yield;
    console.log(x.toUpperCase());
}

let x = bar();
x.next();
x.next(42);

We would have to pass in a string to fix the that arises from x.next(42); . Also, the TypeScript compiler knows that the first call to next() does nothing.

More Accurate Array Spread

With TypeScript 3.6, the transformation of some array spread operators now produce equivalent results when the code is transpiler to ES5 or earlier targets with the --downlevelIteration on.

The flag’s purpose is to transform ES6 iteration constructs like the spread operator and for...of loop to add support for ES6 iteration constructs into code that’s transpiled to something older than ES6.

For example, if we have:

[...Array(3)]

We should get:

[undefined, undefined, undefined]

However, TypeScript versions earlier than 3.6 changes […Array(3)] to Array(3).slice();

Which gets us an empty array with length property set to 3.

Raise Error with Bad Promise Code

TypeScript 3.6 compiler will let us know if we forget to put await before promises in async functions or forget to call then after promises.

For example, if we have:

interface Person {
    name: string;
}

let promise: Promise<Person> = Promise.resolve(<Person>{ name: 'Joe' });
(async () => {
    const person: Person = promise;
})();

Then we get the error:

Property 'name' is missing in type 'Promise<Person>' but required in type 'Person'.

Putting in await before promise would fix the problem:

interface Person {
    name: string;
}

let promise: Promise<Person> = Promise.resolve(<Person>{ name: 'Joe' });
(async () => {
    const person: Person = await promise;
})();

Something writing something like:

(async () => {
      fetch("https://reddit.com/r/javascript.json")
        .json()
})();

will get us the error:

Property 'json' does not exist on type 'Promise<Response>'.(2339)

input.ts(3, 10): Did you forget to use 'await'?

The following will fix the error:

(async () => {
  const response = await fetch("[https://reddit.com/r/javascript.json](https://reddit.com/r/javascript.json)")
  const responseJson = response.json()
})();

Unicode Character Identifiers

Now we can use Unicode characters for identifiers with TypeScript. For example:

const ? = 'foo';

would work with TypeScript 3.6 compiler or later.

get and set Accessors Are Allowed in Declare Statements

We can add get and set to declare statements now. For example, we can write:

declare class Bar {
  get y(): number;
  set y(val: number);
}

The generated type definitions will also emit get and set accessors in TypeScript 3.7 or later.

Merging Class and Constructor Function Declare Statements

With TypeScript 3.6 or later, the compiler is smart enough to merge function constructors and class declare statements with the same name. For example, we can write:

export declare function Person(name: string, age: number): Person;
export declare class Person {
    name: string;
    age: number;
    constructor(name: string, age: number);
}

It knows that the function is a constructor and the class is the same as the function.

The signatures of the constructors in the function and class constructor don’t have to match, so the following:

export declare function Person(name: string): Person;
export declare class Person {
    name: string;
    age: number;
    constructor(name: string, age: number);
}

still works.

Semicolons

Now TypeScript is smart enough to add semicolons automatically to places that requires it by style conventions instead of adding it automatically to every statement.

TypeScript 3.6 is another feature-packed release. It focuses on improving features like inferring types and type checks in generators, more accurate array spread for code emitted in ES5 or earlier.

Also, bad promise code will raise errors, like the ones that missed await or then .

Merging function constructor and class code in declare statements are also supported now.

Unicode characters are now supported in identifiers, and semicolons won’t be added automatically on every line.