[TypeScript] 3.6¶
TypeScript3.6のリリース内容まとめ
Stricter Generators¶
SHOULD NORMAL
ジェネレータ関数がIterableIteratorではなくGeneratorを返すようになった。
それによってジェネレータの型判定が厳密になった。
ジェネレータの返す値がyieldかreturnかを判別できるようになった¶
今まではnext(...)の結果が以下どちらか判別できなかった。
yieldによって返された値returnによって返された値
v3.6からは判別できるようになった。
具体例¶
以下のコードはr.done === trueの結果によってit.next().valueの返却型は絞りこめる。
function* countDown() {
yield 3;
yield 2;
yield 1;
return "Go";
}
const it = countDown();
while (true) {
const r = it.next();
if (r.done) {
console.log(r.value);
break;
} else {
console.log(r.value);
}
}
TypeScript3.5ではr.doneの結果は考慮されないが、TypeScript3.6からは考慮して推論される。
const it = countDown();
while (true) {
const r = it.next();
if (r.done) {
// r.valueは "Go" | 3 | 2 | 1 と推論される
console.log(r.value);
break;
} else {
// r.valueは "Go" | 3 | 2 | 1 と推論される
console.log(r.value);
}
}
const it = countDown();
while (true) {
const r = it.next();
if (r.done) {
// r.valueは string と推論される
console.log(r.value);
break;
} else {
// r.valueは 3 | 2 | 1 と推論される
console.log(r.value);
}
}
詳細¶
v3.6ではnext(...)がIteratorResultを返すようになった。
これはyieldまたはreturnの返却値を意味する。
type IteratorResult<T, TReturn = any> =
| IteratorYieldResult<T>
| IteratorReturnResult<TReturn>;
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
IteratorResultはTagged union typesを使っているため、next(...).doneの結果によってnext(...).valueの型が判別できる。
yieldの戻り値型を考慮できるようになった (anyではなくなった)¶
v3.6では.next(...)の引数に適さない型を入れると、ちゃんとエラーになる。
v3.5ではすべての型を受けつけていた。
具体例¶
以下はnextで渡した引数を繋げて出力していくコード。
出力はsay saytake saytakehundredとなる。
function* stringPrinter() {
let result = "";
while (true) {
const received: string = yield result;
result += received.toLowerCase();
}
}
const it = stringPrinter();
console.log(
it.next().value,
it.next("Say").value,
it.next("TAKE").value,
it.next("hundred").value
);
最後のnextに数値を入れてみる。
v3.5ではエラーにならないが、numberにtoLowerCaseはないため実行時に失敗する。
function* stringPrinter() {
let result = "";
while (true) {
const received: string = yield result;
result += received.toLowerCase();
}
}
const it = stringPrinter();
console.log(
it.next().value,
it.next("Say").value,
it.next("TAKE").value,
// Uncaught TypeError: received.toLowerCase is not a function
it.next(100).value
);
詳細¶
v3.5で実行前エラーにならないのはnextの引数がanyになっているため。
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
v3.6ではGenerator型に対応しており、nextが返すTNextの型と比較できるようになった。
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}
これによって型エラーが検知される。
型 '100' を型 'string' に割り当てることはできません。ts(2345)
More Accurate Array Spread¶
NOT NECESSARY NORMAL
ArrayのSpreadがより正確になった。
[...Array(5)]は[undefined, undefined, undefined, undefined, undefined]を期待するが、--downlevelIterationなしだとv3.5ではArray(5).slice()にトランスパイルされていた。
| TypeScriptバージョン | --downlevelIteration |
[...Array(5)] のトランスパイル結果 |
|---|---|---|
| 3.5 | true | __spread(Array(5)) |
| 3.5 | false | Array(5).slice() |
| 3.6 | true | __spread(Array(5)) |
| 3.6 | false | __spreadArrays(Array(5)) |
v3.6からは[undefined, undefined, undefined, undefined, undefined]を返すようになる。
Improved UX Around Promises¶
NOT NECESSARY EASY
Promiseに関するエラーが分かりやすくなった。
declare function createNumberPromise(): Promise<number>;
declare function showNumber(value: number): void;
async function f() {
showNumber(createNumberPromise());
}
上記はPromiseを解決せず関数の引数に渡してしまった例。
v3.5は真実のみを伝えているが、v3.6ではawait忘れではないかと提案してくれる。
index.ts:5:14 - error TS2345: Argument of type 'Promise<number>' is not assignable to parameter of type 'number'.
5 showNumber(createNumberPromise());
index.ts:5:14 - error TS2345: Argument of type 'Promise<number>' is not assignable to parameter of type 'number'.
5 showNumber(createNumberPromise());
~~~~~~~~~~~~~~~~~~~~~
index.ts:5:14
5 showNumber(createNumberPromise());
~~~~~~~~~~~~~~~~~~~~~
Did you forget to use 'await'?
Promise<T>をawaitしないままTに存在するプロパティを呼び出したときも同様の提案をしてくれる。
...がこれはv3.5の時点から対応されていた。(リリースノートの間違い?)
Better Unicode Support for Identifiers¶
NOT NECESSARY EASY
targetにes2015以降を指定すると、Unicodeがよりサポートされるようになった。
import.meta Support in SystemJS¶
UNKNOWN EASY
moduleターゲットがsystemのときimport.metaをcontext.metaに変換するようになった。
何が嬉しいかがわからない..
get and set Accessors Are Allowed in Ambient Contexts¶
HAD BETTER EASY
declareを使ったクラスや.d.tsの型定義でgetterやsetterを書けるようになった。
// v3.5だとエラーになる
declare class Human {
get name(): string;
set age(value: number);
}
Ambient Classes and Functions Can Merge¶
HAD BETTER EASY
declareを使ったClassとFunctionの宣言をマージできるようになった。
v3.5では識別子重複エラーになる以下のようなコードが書ける。
export declare function Human(id: number, name: string): Human;
export declare class Human {
id: number;
name: string;
constructor(id: number, name: string);
}
これはCallable constructor patternを実現できており、newなしでインスタンス生成させたい場合に便利である。
const jiro = Human(1, "jiro");
APIs to Support --build and --incremental¶
NOT NECESSARY EASY
--build1や--incremental2オプションに相当するコンパイラAPIがサポートされるようになった。
GulpやWebpackなど3rd partyのビルドツールでもこれらの恩恵を得られるようになる。
Semicolon-Aware Code Edits¶
HAD BETTER EASY
IDEでquick fixやrefactoringを実行したとき、セミコロンを付けるかどうかの挙動が変わった。
| バージョン | セミコロン |
|---|---|
| v3.5 | 必ずセミコロンを付ける |
| v3.6 | 対象ファイルのセミコロン利用状況を見て判断する |
セミコロンを使用しないスタイルの人にとってはPrettierなどで変更する手間が省ける。
Smarter Auto-Import Syntax¶
HAD BETTER EASY
IDEでAuto-importを実行したとき、どの形式でimport文を挿入するかの挙動が変わった。
| バージョン | セミコロン |
|---|---|
| v3.5 | ECMAScript module syntaxに従う |
| v3.6 | 対象ファイルのimport状況を見て判断する |
ECMAScript module syntaxでimportしていないプロジェクトは変更の手間が省ける。
New TypeScript Playground¶
HAD BETTER EASY
TypeScript Playgroundが新しくなった。
targetが指定できるようになったstrict系フラグ- JavaScript対応 (
allowJS,checkJs)