Categories
TypeScript

Cool New Features Released with TypeScript 3.6

Spread the love

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.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *