[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
--build
1や--incremental
2オプションに相当するコンパイラ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
)