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.